diff options
author | Sebastian Holtermann <sebholt@xwmw.org> | 2017-09-02 09:07:30 (GMT) |
---|---|---|
committer | Sebastian Holtermann <sebholt@xwmw.org> | 2017-09-20 21:21:01 (GMT) |
commit | 15346bec1930437cf9eb4fd32b6733790be2cc58 (patch) | |
tree | ebb33854a7b5b878cda0fb7c996b15b506c7576e /Source | |
parent | 95df03a1d44d48b5407dd0ddc9e618ce2f17762b (diff) | |
download | CMake-15346bec1930437cf9eb4fd32b6733790be2cc58.zip CMake-15346bec1930437cf9eb4fd32b6733790be2cc58.tar.gz CMake-15346bec1930437cf9eb4fd32b6733790be2cc58.tar.bz2 |
Autogen: Refactor cmQtAutoGenerators
The refactoring of cmQtAutoGenerators serializes the program flow and
makes it less jumpy in terms of function calling.
Instead of keeping and passing multiple std::vectors and std::maps in
function arguments, single lists with job descriptions are used,
one job list for MOC, UIC, RCC respectively.
Several utility functions and methods were replaced with scoped lambdas
and the remaining methods were sorted by their scope (MOC, UIC, RCC).
Error and warning messages were refactored to be more verbose
about the problem at hand.
The source parsing algorithms were rewritten in large parts.
In the process a lack of functionality of CMAKE_AUTOMOC_DEPEND_FILTERS
was discovered and fixed. CMAKE_AUTOMOC_DEPEND_FILTERS did not extract
dependency file names from headers that were not in the target sources
but were registered to AUTOMOC by a `#include "moc_<NAME>.cpp"` statement.
A test for this use case is provided in a follow up commit.
Diffstat (limited to 'Source')
-rw-r--r-- | Source/cmQtAutoGenerators.cxx | 2017 | ||||
-rw-r--r-- | Source/cmQtAutoGenerators.h | 239 |
2 files changed, 1156 insertions, 1100 deletions
diff --git a/Source/cmQtAutoGenerators.cxx b/Source/cmQtAutoGenerators.cxx index 8c03045..8453198 100644 --- a/Source/cmQtAutoGenerators.cxx +++ b/Source/cmQtAutoGenerators.cxx @@ -8,6 +8,7 @@ #include <algorithm> #include <array> #include <list> +#include <memory> #include <sstream> #include <string.h> #include <utility> @@ -35,14 +36,23 @@ static const char* SettingsKeyRcc = "AM_RCC_SETTINGS_HASH"; // -- Static functions -static std::string QuotedCommand(const std::vector<std::string>& command) +static std::string HeadLine(std::string const& title) +{ + std::string head = title; + head += '\n'; + head.append(head.size() - 1, '-'); + head += '\n'; + return head; +} + +static std::string QuotedCommand(std::vector<std::string> const& command) { std::string res; for (std::string const& item : command) { if (!res.empty()) { res.push_back(' '); } - const std::string cesc = cmQtAutoGen::Quoted(item); + std::string const cesc = cmQtAutoGen::Quoted(item); if (item.empty() || (cesc.size() > (item.size() + 2)) || (cesc.find(' ') != std::string::npos)) { res += cesc; @@ -53,7 +63,7 @@ static std::string QuotedCommand(const std::vector<std::string>& command) return res; } -static std::string SubDirPrefix(const std::string& fileName) +static std::string SubDirPrefix(std::string const& fileName) { std::string res(cmSystemTools::GetFilenamePath(fileName)); if (!res.empty()) { @@ -62,17 +72,29 @@ static std::string SubDirPrefix(const std::string& fileName) return res; } -static bool ReadAll(std::string& content, const std::string& filename) +static bool ReadFile(std::string& content, std::string const& filename, + std::string* error = nullptr) { bool success = false; - { - cmsys::ifstream ifs(filename.c_str()); + 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) { - std::ostringstream osst; - osst << ifs.rdbuf(); - content = osst.str(); - success = true; + 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("The file does not exist."); } return success; } @@ -81,17 +103,17 @@ static bool ReadAll(std::string& content, const std::string& filename) * @brief Tests if buildFile doesn't exist or is older than sourceFile * @return True if buildFile doesn't exist or is older than sourceFile */ -static bool FileAbsentOrOlder(const std::string& buildFile, - const std::string& sourceFile) +static bool FileAbsentOrOlder(std::string const& buildFile, + std::string const& sourceFile) { int result = 0; bool success = cmSystemTools::FileTimeCompare(buildFile, sourceFile, &result); - return (!success || (result <= 0)); + return (!success || (result < 0)); } -static bool ListContains(const std::vector<std::string>& list, - const std::string& entry) +static bool ListContains(std::vector<std::string> const& list, + std::string const& entry) { return (std::find(list.begin(), list.end(), entry) != list.end()); } @@ -99,24 +121,20 @@ static bool ListContains(const std::vector<std::string>& list, // -- Class methods cmQtAutoGenerators::cmQtAutoGenerators() - : Verbose(cmSystemTools::HasEnv("VERBOSE")) + : IncludeProjectDirsBefore(false) + , Verbose(cmSystemTools::HasEnv("VERBOSE")) , ColorOutput(true) , MocSettingsChanged(false) , MocPredefsChanged(false) - , MocRunFailed(false) + , MocRelaxedMode(false) , UicSettingsChanged(false) - , UicRunFailed(false) , RccSettingsChanged(false) - , RccRunFailed(false) { - - std::string colorEnv; - cmSystemTools::GetEnv("COLOR", colorEnv); - if (!colorEnv.empty()) { - if (cmSystemTools::IsOn(colorEnv.c_str())) { - this->ColorOutput = true; - } else { - this->ColorOutput = false; + { + std::string colorEnv; + cmSystemTools::GetEnv("COLOR", colorEnv); + if (!colorEnv.empty()) { + this->ColorOutput = cmSystemTools::IsOn(colorEnv.c_str()); } } @@ -134,8 +152,8 @@ cmQtAutoGenerators::cmQtAutoGenerators() "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]"); } -bool cmQtAutoGenerators::Run(const std::string& targetDirectory, - const std::string& config) +bool cmQtAutoGenerators::Run(std::string const& targetDirectory, + std::string const& config) { cmake cm(cmake::RoleScript); cm.SetHomeOutputDirectory(targetDirectory); @@ -147,16 +165,14 @@ bool cmQtAutoGenerators::Run(const std::string& targetDirectory, snapshot.GetDirectory().SetCurrentBinary(targetDirectory); snapshot.GetDirectory().SetCurrentSource(targetDirectory); - std::unique_ptr<cmMakefile> makefile(new cmMakefile(&gg, snapshot)); + auto makefile = cm::make_unique<cmMakefile>(&gg, snapshot); gg.SetCurrentMakefile(makefile.get()); bool success = false; - if (this->ReadAutogenInfoFile(makefile.get(), targetDirectory, config)) { - // Read old settings + if (this->InitInfoFile(makefile.get(), targetDirectory, config)) { + // Read latest settings this->SettingsFileRead(makefile.get()); - // Init and run - this->Init(makefile.get()); - if (this->RunAutogen()) { + if (this->Process()) { // Write current settings if (this->SettingsFileWrite()) { success = true; @@ -166,46 +182,11 @@ bool cmQtAutoGenerators::Run(const std::string& targetDirectory, return success; } -bool cmQtAutoGenerators::MocDependFilterPush(const std::string& key, - const std::string& regExp) +bool cmQtAutoGenerators::InitInfoFile(cmMakefile* makefile, + std::string const& targetDirectory, + std::string const& config) { - std::string error; - if (!key.empty()) { - if (!regExp.empty()) { - KeyRegExp filter; - filter.Key = key; - if (filter.RegExp.compile(regExp)) { - this->MocDependFilters.push_back(std::move(filter)); - } else { - error = "Regular expression compiling failed"; - } - } else { - error = "Regular expression is empty"; - } - } else { - error = "Key is empty"; - } - if (!error.empty()) { - std::string emsg = "AUTOMOC_DEPEND_FILTERS: "; - emsg += error; - emsg += "\n"; - emsg += " Key: "; - emsg += cmQtAutoGen::Quoted(key); - emsg += "\n"; - emsg += " RegExp: "; - emsg += cmQtAutoGen::Quoted(regExp); - emsg += "\n"; - this->LogError(cmQtAutoGen::MOC, emsg); - return false; - } - return true; -} - -bool cmQtAutoGenerators::ReadAutogenInfoFile( - cmMakefile* makefile, const std::string& targetDirectory, - const std::string& config) -{ - // Lambdas + // Utility lambdas auto InfoGet = [makefile](const char* key) { return makefile->GetSafeDefinition(key); }; @@ -221,7 +202,7 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile( [makefile](const char* key) -> std::vector<std::vector<std::string>> { std::vector<std::vector<std::string>> lists; { - const std::string value = makefile->GetSafeDefinition(key); + 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); @@ -266,16 +247,18 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile( return list; }; - std::string filename(cmSystemTools::CollapseFullPath(targetDirectory)); - cmSystemTools::ConvertToUnixSlashes(filename); - filename += "/AutogenInfo.cmake"; + this->InfoFile = cmSystemTools::CollapseFullPath(targetDirectory); + cmSystemTools::ConvertToUnixSlashes(this->InfoFile); + this->InfoFile += "/AutogenInfo.cmake"; - if (!makefile->ReadListFile(filename.c_str())) { - this->LogFileError(cmQtAutoGen::GEN, filename, "File processing failed"); + if (!makefile->ReadListFile(this->InfoFile.c_str())) { + this->LogFileError(cmQtAutoGen::GEN, this->InfoFile, + "File processing failed"); return false; } // -- Meta + this->HeaderExtensions = makefile->GetCMakeInstance()->GetHeaderExtensions(); this->ConfigSuffix = InfoGetConfig("AM_CONFIG_SUFFIX"); // - Old settings file @@ -296,12 +279,10 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile( InfoGetBool("AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE"); this->AutogenBuildDir = InfoGet("AM_BUILD_DIR"); if (this->AutogenBuildDir.empty()) { - this->LogFileError(cmQtAutoGen::GEN, filename, + this->LogFileError(cmQtAutoGen::GEN, this->InfoFile, "Autogen build directory missing"); return false; } - this->Sources = InfoGetList("AM_SOURCES"); - this->Headers = InfoGetList("AM_HEADERS"); // - Qt environment this->QtMajorVersion = InfoGet("AM_QT_VERSION_MAJOR"); @@ -312,7 +293,8 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile( // Check Qt version if ((this->QtMajorVersion != "4") && (this->QtMajorVersion != "5")) { - this->LogFileError(cmQtAutoGen::GEN, filename, "Unsupported Qt version: " + + this->LogFileError(cmQtAutoGen::GEN, this->InfoFile, + "Unsupported Qt version: " + cmQtAutoGen::Quoted(this->QtMajorVersion)); return false; } @@ -323,7 +305,7 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile( this->MocDefinitions = InfoGetConfigList("AM_MOC_DEFINITIONS"); #ifdef _WIN32 { - const std::string win32("WIN32"); + std::string const win32("WIN32"); if (!ListContains(this->MocDefinitions, win32)) { this->MocDefinitions.push_back(win32); } @@ -333,7 +315,7 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile( this->MocOptions = InfoGetList("AM_MOC_OPTIONS"); this->MocRelaxedMode = InfoGetBool("AM_MOC_RELAXED_MODE"); { - const std::vector<std::string> MocMacroNames = + std::vector<std::string> const MocMacroNames = InfoGetList("AM_MOC_MACRO_NAMES"); for (std::string const& item : MocMacroNames) { this->MocMacroFilters.emplace_back( @@ -341,7 +323,7 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile( } } { - const std::vector<std::string> mocDependFilters = + std::vector<std::string> const mocDependFilters = InfoGetList("AM_MOC_DEPEND_FILTERS"); // Insert Q_PLUGIN_METADATA dependency filter if (this->QtMajorVersion != "4") { @@ -361,7 +343,7 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile( } } else { this->LogFileError( - cmQtAutoGen::MOC, filename, + cmQtAutoGen::MOC, this->InfoFile, "AUTOMOC_DEPEND_FILTERS list size is not a multiple of 2"); return false; } @@ -378,22 +360,21 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile( auto sources = InfoGetList("AM_UIC_OPTIONS_FILES"); auto options = InfoGetLists("AM_UIC_OPTIONS_OPTIONS"); // Compare list sizes - if (sources.size() == options.size()) { - auto fitEnd = sources.cend(); - auto fit = sources.begin(); - auto oit = options.begin(); - while (fit != fitEnd) { - this->UicOptions[*fit] = std::move(*oit); - ++fit; - ++oit; - } - } else { + if (sources.size() != options.size()) { std::ostringstream ost; ost << "files/options lists sizes missmatch (" << sources.size() << "/" << options.size() << ")"; - this->LogFileError(cmQtAutoGen::UIC, filename, ost.str()); + this->LogFileError(cmQtAutoGen::UIC, this->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); + ++fit; + ++oit; + } } } @@ -409,24 +390,23 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile( std::ostringstream ost; ost << "sources, builds lists sizes missmatch (" << sources.size() << "/" << builds.size() << ")"; - this->LogFileError(cmQtAutoGen::RCC, filename, ost.str()); + this->LogFileError(cmQtAutoGen::RCC, this->InfoFile, ost.str()); return false; } if (sources.size() != options.size()) { std::ostringstream ost; ost << "sources, options lists sizes missmatch (" << sources.size() << "/" << options.size() << ")"; - this->LogFileError(cmQtAutoGen::RCC, filename, ost.str()); + this->LogFileError(cmQtAutoGen::RCC, this->InfoFile, ost.str()); return false; } if (sources.size() != inputs.size()) { std::ostringstream ost; ost << "sources, inputs lists sizes missmatch (" << sources.size() << "/" << inputs.size() << ")"; - this->LogFileError(cmQtAutoGen::RCC, filename, ost.str()); + this->LogFileError(cmQtAutoGen::RCC, this->InfoFile, ost.str()); return false; } - { auto srcItEnd = sources.end(); auto srcIt = sources.begin(); @@ -445,6 +425,156 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile( } } + // 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; + } + }; + + // Add header jobs + for (std::string& hdr : InfoGetList("AM_HEADERS")) { + AddJob(this->HeaderJobs, std::move(hdr)); + } + // 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); + 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; + } + } + } + } + // Add Source jobs + for (std::string& src : sources) { + AddJob(this->SourceJobs, std::move(src)); + } + } + } + + // Init derived information + // ------------------------ + + // Init file path checksum generator + this->FilePathChecksum.setupParentDirs( + this->CurrentSourceDir, this->CurrentBinaryDir, this->ProjectSourceDir, + this->ProjectBinaryDir); + + // include directory + this->AutogenIncludeDir = "include"; + this->AutogenIncludeDir += this->ConfigSuffix; + this->AutogenIncludeDir += "/"; + + if (this->MocEnabled()) { + // Mocs compilation file + this->MocCompFileRel = "mocs_compilation.cpp"; + this->MocCompFileAbs = cmSystemTools::CollapseCombinedPath( + this->AutogenBuildDir, this->MocCompFileRel); + + // Moc predefs file + if (!this->MocPredefsCmd.empty()) { + this->MocPredefsFileRel = "moc_predefs"; + this->MocPredefsFileRel += this->ConfigSuffix; + this->MocPredefsFileRel += ".h"; + this->MocPredefsFileAbs = cmSystemTools::CollapseCombinedPath( + this->AutogenBuildDir, this->MocPredefsFileRel); + } + + // Sort include directories on demand + if (this->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()); + // Append project directories only + { + std::array<std::string const*, 2> const movePaths = { + { &this->ProjectBinaryDir, &this->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); + it = includes.erase(it); + } else { + ++it; + } + } + } + } + // Append remaining directories + this->MocIncludePaths.insert(this->MocIncludePaths.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); + // Extract framework path + if (cmHasLiteralSuffix(path, ".framework/Headers")) { + // Go up twice to get to the framework root + std::vector<std::string> pathComponents; + cmSystemTools::SplitPath(path, pathComponents); + std::string frameworkPath = cmSystemTools::JoinPath( + pathComponents.begin(), pathComponents.end() - 2); + frameworkPaths.insert(frameworkPath); + } + } + // Append framework includes + for (std::string const& path : frameworkPaths) { + this->MocIncludes.push_back("-F"); + this->MocIncludes.push_back(path); + } + } + // Setup single list with all options + { + // Add includes + this->MocAllOptions.insert(this->MocAllOptions.end(), + this->MocIncludes.begin(), + this->MocIncludes.end()); + // Add definitions + for (std::string const& def : this->MocDefinitions) { + this->MocAllOptions.push_back("-D" + def); + } + // Add options + this->MocAllOptions.insert(this->MocAllOptions.end(), + this->MocOptions.begin(), + this->MocOptions.end()); + } + } + return true; } @@ -453,16 +583,12 @@ void cmQtAutoGenerators::SettingsFileRead(cmMakefile* makefile) // Compose current settings strings { cmCryptoHash crypt(cmCryptoHash::AlgoSHA256); - const std::string sep(" ~~~ "); + std::string const sep(" ~~~ "); if (this->MocEnabled()) { std::string str; str += this->MocExecutable; str += sep; - str += cmJoin(this->MocDefinitions, ";"); - str += sep; - str += cmJoin(this->MocIncludePaths, ";"); - str += sep; - str += cmJoin(this->MocOptions, ";"); + str += cmJoin(this->MocAllOptions, ";"); str += sep; str += this->IncludeProjectDirsBefore ? "TRUE" : "FALSE"; str += sep; @@ -503,7 +629,7 @@ void cmQtAutoGenerators::SettingsFileRead(cmMakefile* makefile) // Read old settings if (makefile->ReadListFile(this->SettingsFile.c_str())) { { - auto SMatch = [makefile](const char* key, const std::string& value) { + auto SMatch = [makefile](const char* key, std::string const& value) { return (value == makefile->GetSafeDefinition(key)); }; if (!SMatch(SettingsKeyMoc, this->SettingsStringMoc)) { @@ -519,7 +645,7 @@ void cmQtAutoGenerators::SettingsFileRead(cmMakefile* makefile) // 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->AnySettingsChanged()) { + if (this->SettingsChanged()) { cmSystemTools::RemoveFile(this->SettingsFile); } } else { @@ -534,7 +660,7 @@ bool cmQtAutoGenerators::SettingsFileWrite() { bool success = true; // Only write if any setting changed - if (this->AnySettingsChanged()) { + if (this->SettingsChanged()) { if (this->Verbose) { this->LogInfo(cmQtAutoGen::GEN, "Writing settings file " + cmQtAutoGen::Quoted(this->SettingsFile)); @@ -543,7 +669,7 @@ bool cmQtAutoGenerators::SettingsFileWrite() std::string settings; { auto SettingAppend = [&settings](const char* key, - const std::string& value) { + std::string const& value) { settings += "set("; settings += key; settings += " "; @@ -557,7 +683,7 @@ bool cmQtAutoGenerators::SettingsFileWrite() // Write settings file if (!this->FileWrite(cmQtAutoGen::GEN, this->SettingsFile, settings)) { this->LogFileError(cmQtAutoGen::GEN, this->SettingsFile, - "File writing failed"); + "Settings file writing failed"); // Remove old settings file to trigger a full rebuild on the next run cmSystemTools::RemoveFile(this->SettingsFile); success = false; @@ -566,89 +692,7 @@ bool cmQtAutoGenerators::SettingsFileWrite() return success; } -void cmQtAutoGenerators::Init(cmMakefile* makefile) -{ - // Mocs compilation file - this->MocCompFileRel = "mocs_compilation.cpp"; - this->MocCompFileAbs = cmSystemTools::CollapseCombinedPath( - this->AutogenBuildDir, this->MocCompFileRel); - - // Mocs include directory - this->AutogenIncludeDir = "include"; - this->AutogenIncludeDir += this->ConfigSuffix; - this->AutogenIncludeDir += "/"; - - // Moc predefs file - if (!this->MocPredefsCmd.empty()) { - this->MocPredefsFileRel = "moc_predefs"; - this->MocPredefsFileRel += this->ConfigSuffix; - this->MocPredefsFileRel += ".h"; - this->MocPredefsFileAbs = cmSystemTools::CollapseCombinedPath( - this->AutogenBuildDir, this->MocPredefsFileRel); - } - - // Init file path checksum generator - FPathChecksum.setupParentDirs(this->CurrentSourceDir, this->CurrentBinaryDir, - this->ProjectSourceDir, - this->ProjectBinaryDir); - - // Acquire header extensions - this->HeaderExtensions = makefile->GetCMakeInstance()->GetHeaderExtensions(); - - // Sort include directories on demand - if (this->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()); - // Append project directories only - { - const std::array<const std::string*, 2> movePaths = { - { &this->ProjectBinaryDir, &this->ProjectSourceDir } - }; - for (const std::string* ppath : movePaths) { - std::list<std::string>::iterator it = includes.begin(); - while (it != includes.end()) { - const std::string& path = *it; - if (cmSystemTools::StringStartsWith(path, ppath->c_str())) { - this->MocIncludePaths.push_back(path); - it = includes.erase(it); - } else { - ++it; - } - } - } - } - // Append remaining directories - this->MocIncludePaths.insert(this->MocIncludePaths.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); - // Extract framework path - if (cmHasLiteralSuffix(path, ".framework/Headers")) { - // Go up twice to get to the framework root - std::vector<std::string> pathComponents; - cmSystemTools::SplitPath(path, pathComponents); - std::string frameworkPath = cmSystemTools::JoinPath( - pathComponents.begin(), pathComponents.end() - 2); - frameworkPaths.insert(frameworkPath); - } - } - // Append framework includes - for (std::string const& path : frameworkPaths) { - this->MocIncludes.push_back("-F"); - this->MocIncludes.push_back(path); - } - } -} - -bool cmQtAutoGenerators::RunAutogen() +bool cmQtAutoGenerators::Process() { // 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 @@ -660,7 +704,7 @@ bool cmQtAutoGenerators::RunAutogen() // Create AUTOGEN include directory { - const std::string incDirAbs = cmSystemTools::CollapseCombinedPath( + std::string const incDirAbs = cmSystemTools::CollapseCombinedPath( this->AutogenBuildDir, this->AutogenIncludeDir); if (!cmSystemTools::MakeDirectory(incDirAbs)) { this->LogFileError(cmQtAutoGen::GEN, incDirAbs, @@ -669,45 +713,28 @@ bool cmQtAutoGenerators::RunAutogen() } } - // key = moc source filepath, value = moc output filepath - std::map<std::string, std::string> mocsIncluded; - std::map<std::string, std::string> mocsNotIncluded; - std::map<std::string, std::set<std::string>> mocDepends; - std::map<std::string, std::vector<std::string>> uisIncluded; - // collects all headers which may need to be mocced - std::set<std::string> mocHeaderFiles; - std::set<std::string> uicHeaderFiles; - - // Parse sources - for (std::string const& src : this->Sources) { - // Parse source file for MOC/UIC - if (!this->ParseSourceFile(src, mocsIncluded, mocDepends, uisIncluded, - this->MocRelaxedMode)) { + // Parse source files + for (const auto& item : this->SourceJobs) { + if (!this->ParseSourceFile(item.first, item.second)) { return false; } - // Find additional headers - this->SearchHeadersForSourceFile(src, mocHeaderFiles, uicHeaderFiles); } - - // Parse headers - for (std::string const& hdr : this->Headers) { - if (!this->MocSkip(hdr)) { - mocHeaderFiles.insert(hdr); - } - if (!this->UicSkip(hdr)) { - uicHeaderFiles.insert(hdr); + // Parse header files + for (const auto& item : this->HeaderJobs) { + if (!this->ParseHeaderFile(item.first, item.second)) { + return false; } } - if (!this->ParseHeaders(mocHeaderFiles, uicHeaderFiles, mocsIncluded, - mocsNotIncluded, mocDepends, uisIncluded)) { + // Read missing dependency information + if (!this->ParsePostprocess()) { return false; - }; + } // Generate files - if (!this->MocGenerateAll(mocsIncluded, mocsNotIncluded, mocDepends)) { + if (!this->MocGenerateAll()) { return false; } - if (!this->UicGenerateAll(uisIncluded)) { + if (!this->UicGenerateAll()) { return false; } if (!this->RccGenerateAll()) { @@ -718,87 +745,100 @@ bool cmQtAutoGenerators::RunAutogen() } /** - * @brief Tests if the C++ content requires moc processing - * @return True if moc is required + * @return True on success */ -bool cmQtAutoGenerators::MocRequired(const std::string& contentText, - std::string* macroName) +bool cmQtAutoGenerators::ParseSourceFile(std::string const& absFilename, + const SourceJob& job) { - 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; + std::string contentText; + std::string error; + bool success = ReadFile(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 false; + return success; } -void cmQtAutoGenerators::MocFindDepends( - const std::string& absFilename, const std::string& contentText, - std::map<std::string, std::set<std::string>>& mocDepends) +/** + * @return True on success + */ +bool cmQtAutoGenerators::ParseHeaderFile(std::string const& absFilename, + const SourceJob& job) { - for (KeyRegExp& filter : this->MocDependFilters) { - // Run a simple find string operation before the expensive - // regular expression check - if (contentText.find(filter.Key) != std::string::npos) { - // Run regular expression check loop - const std::string sourcePath = SubDirPrefix(absFilename); - const char* contentChars = contentText.c_str(); - while (filter.RegExp.find(contentChars)) { - // Evaluate match - const std::string match = filter.RegExp.match(1); - if (!match.empty()) { - // Find the dependency file - std::string incFile; - if (this->MocFindIncludedFile(incFile, sourcePath, match)) { - mocDepends[absFilename].insert(incFile); - if (this->Verbose) { - this->LogInfo(cmQtAutoGen::MOC, "Found dependency:\n " + - cmQtAutoGen::Quoted(absFilename) + "\n " + - cmQtAutoGen::Quoted(incFile)); - } - } else { - this->LogFileWarning(cmQtAutoGen::MOC, absFilename, - "Could not find dependency file " + - cmQtAutoGen::Quoted(match)); - } - } - contentChars += filter.RegExp.end(); + std::string contentText; + std::string error; + bool success = ReadFile(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; } /** - * @brief Tests if the file should be ignored for moc scanning - * @return True if the file should be ignored + * @return True on success */ -bool cmQtAutoGenerators::MocSkip(const std::string& absFilename) const +bool cmQtAutoGenerators::ParsePostprocess() { - if (this->MocEnabled()) { - // Test if the file name is on the skip list - if (!ListContains(this->MocSkipList, absFilename)) { - return false; + bool success = true; + // Read missin dependecies + for (auto& item : this->MocJobsIncluded) { + if (!item->DependsValid) { + std::string content; + std::string error; + if (ReadFile(content, item->SourceFile, &error)) { + this->MocFindDepends(item->SourceFile, content, item->Depends); + item->DependsValid = true; + } 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; + } } } - return true; + return success; } /** - * @brief Tests if the file name is in the skip list + * @brief Tests if the file should be ignored for moc scanning + * @return True if the file should be ignored */ -bool cmQtAutoGenerators::UicSkip(const std::string& absFilename) const +bool cmQtAutoGenerators::MocSkip(std::string const& absFilename) const { - if (this->UicEnabled()) { + if (this->MocEnabled()) { // Test if the file name is on the skip list - if (!ListContains(this->UicSkipList, absFilename)) { + if (!ListContains(this->MocSkipList, absFilename)) { return false; } } @@ -806,54 +846,29 @@ bool cmQtAutoGenerators::UicSkip(const std::string& absFilename) const } /** - * @return True on success + * @brief Tests if the C++ content requires moc processing + * @return True if moc is required */ -bool cmQtAutoGenerators::ParseSourceFile( - const std::string& absFilename, - std::map<std::string, std::string>& mocsIncluded, - std::map<std::string, std::set<std::string>>& mocDepends, - std::map<std::string, std::vector<std::string>>& uisIncluded, bool relaxed) +bool cmQtAutoGenerators::MocRequired(std::string const& contentText, + std::string* macroName) { - std::string contentText; - bool success = ReadAll(contentText, absFilename); - if (success) { - if (!contentText.empty()) { - // Parse source contents for MOC - if (success && !this->MocSkip(absFilename)) { - success = this->MocParseSourceContent( - absFilename, contentText, mocsIncluded, mocDepends, relaxed); - } - // Parse source contents for UIC - if (success && !this->UicSkip(absFilename)) { - this->UicParseContent(absFilename, contentText, uisIncluded); + 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; } - } else { - this->LogFileWarning(cmQtAutoGen::GEN, absFilename, "The file is empty"); - } - } else { - this->LogFileError(cmQtAutoGen::GEN, absFilename, "Could not read file"); - } - return success; -} - -void cmQtAutoGenerators::UicParseContent( - const std::string& absFilename, const std::string& contentText, - std::map<std::string, std::vector<std::string>>& uisIncluded) -{ - if (this->Verbose) { - this->LogInfo(cmQtAutoGen::UIC, "Checking " + absFilename); - } - - const char* contentChars = contentText.c_str(); - if (strstr(contentChars, "ui_") != nullptr) { - while (this->UicRegExpInclude.find(contentChars)) { - uisIncluded[absFilename].push_back(this->UicRegExpInclude.match(1)); - contentChars += this->UicRegExpInclude.end(); } } + return false; } -std::string cmQtAutoGenerators::MocMacroNamesString() const +std::string cmQtAutoGenerators::MocStringMacros() const { std::string res; const auto itB = this->MocMacroFilters.cbegin(); @@ -875,339 +890,479 @@ std::string cmQtAutoGenerators::MocMacroNamesString() const return res; } -std::string cmQtAutoGenerators::MocHeaderSuffixesString() const +std::string cmQtAutoGenerators::MocStringHeaders( + std::string const& fileBase) const { - std::string res = ".{"; + std::string res = fileBase; + res += ".{"; res += cmJoin(this->HeaderExtensions, ","); res += "}"; return res; } +std::string cmQtAutoGenerators::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 cmQtAutoGenerators::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 cmQtAutoGenerators::MocDependFilterPush(std::string const& key, + std::string const& regExp) +{ + std::string error; + if (!key.empty()) { + if (!regExp.empty()) { + KeyRegExp filter; + filter.Key = key; + if (filter.RegExp.compile(regExp)) { + this->MocDependFilters.push_back(std::move(filter)); + } else { + error = "Regular expression compiling failed"; + } + } else { + error = "Regular expression is empty"; + } + } else { + error = "Key is empty"; + } + if (!error.empty()) { + std::string emsg = "AUTOMOC_DEPEND_FILTERS: "; + emsg += error; + emsg += "\n"; + emsg += " Key: "; + emsg += cmQtAutoGen::Quoted(key); + emsg += "\n"; + emsg += " RegExp: "; + emsg += cmQtAutoGen::Quoted(regExp); + emsg += "\n"; + this->LogError(cmQtAutoGen::MOC, emsg); + return false; + } + return true; +} + +void cmQtAutoGenerators::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->Verbose) { + this->LogInfo(cmQtAutoGen::MOC, "Found dependency:\n " + + cmQtAutoGen::Quoted(absFilename) + "\n " + + cmQtAutoGen::Quoted(incFile)); + } + } else { + this->LogFileWarning(cmQtAutoGen::MOC, absFilename, + "Could not find dependency file " + + cmQtAutoGen::Quoted(match)); + } + } + } +} + /** * @return True on success */ -bool cmQtAutoGenerators::MocParseSourceContent( - const std::string& absFilename, const std::string& contentText, - std::map<std::string, std::string>& mocsIncluded, - std::map<std::string, std::set<std::string>>& mocDepends, bool relaxed) +bool cmQtAutoGenerators::MocParseSourceContent(std::string const& absFilename, + std::string const& contentText) { if (this->Verbose) { - this->LogInfo(cmQtAutoGen::MOC, "Checking " + absFilename); - } + 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)); + }; - const std::string scanFileDir = SubDirPrefix(absFilename); - const std::string scanFileBase = - cmSystemTools::GetFilenameWithoutLastExtension(absFilename); + 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 ownMocUnderscoreInclude; - std::string ownMocUnderscoreHeader; - - // first a simple string check for "moc" is *much* faster than the regexp, - // and if the string search already fails, we don't have to try the - // expensive regexp - const char* contentChars = contentText.c_str(); - if (strstr(contentChars, "moc") != nullptr) { - // Iterate over all included moc files - while (this->MocRegExpInclude.find(contentChars)) { - const std::string incString = this->MocRegExpInclude.match(1); - // Basename of the moc include - const std::string incDir(SubDirPrefix(incString)); - const std::string incBase = - cmSystemTools::GetFilenameWithoutLastExtension(incString); - - // If the moc include is of the moc_foo.cpp style we expect - // the Q_OBJECT class declaration in a header file. - // If the moc include is of the foo.moc style we need to look for - // a Q_OBJECT macro in the current source file, if it contains the - // macro we generate the moc file from the source file. - if (cmHasLiteralPrefix(incBase, "moc_")) { - // Include: moc_FOO.cxx - // Remove the moc_ part - const std::string incRealBase = incBase.substr(4); - const std::string headerToMoc = - this->MocFindHeader(scanFileDir, incDir + incRealBase); - if (!headerToMoc.empty()) { - if (!this->MocSkip(headerToMoc)) { - // Register moc job - mocsIncluded[headerToMoc] = incString; - this->MocFindDepends(headerToMoc, contentText, mocDepends); - // Store meta information for relaxed mode - if (relaxed && (incRealBase == scanFileBase)) { - ownMocUnderscoreInclude = incString; - ownMocUnderscoreHeader = headerToMoc; - } - } - } else { - std::ostringstream ost; - ost << "The file includes the moc file " - << cmQtAutoGen::Quoted(incString) - << ", but could not find header "; - ost << cmQtAutoGen::Quoted(incRealBase + - this->MocHeaderSuffixesString()); - this->LogFileError(cmQtAutoGen::MOC, absFilename, ost.str()); - return 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 { - // Include: FOO.moc - bool ownDotMoc = (incBase == scanFileBase); - std::string fileToMoc; - if (relaxed) { - // Mode: Relaxed - if (selfRequiresMoc && ownDotMoc) { - // Include self - fileToMoc = absFilename; - ownDotMocIncluded = true; - } else { - // In relaxed mode try to find a header instead but issue a warning - const std::string headerToMoc = - this->MocFindHeader(scanFileDir, incDir + incBase); - if (!headerToMoc.empty()) { - if (!this->MocSkip(headerToMoc)) { - // This is for KDE4 compatibility: - fileToMoc = headerToMoc; - - auto quoted_inc = cmQtAutoGen::Quoted(incString); - auto quoted_header = cmQtAutoGen::Quoted(headerToMoc); - auto quoted_base = - cmQtAutoGen::Quoted("moc_" + incBase + ".cpp"); - if (!selfRequiresMoc) { - if (ownDotMoc) { - std::ostringstream ost; - ost << "The file includes the moc file " << quoted_inc - << ", but does not contain a " - << this->MocMacroNamesString() << " macro.\n" - << "Running moc on\n" - << " " << quoted_header << "!\n" - << "Better include " << quoted_base - << " for a compatibility with strict mode.\n" - "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n"; - this->LogFileWarning(cmQtAutoGen::MOC, absFilename, - ost.str()); - } else if (!ownDotMoc) { - std::ostringstream ost; - ost << "The file includes the moc file " << quoted_inc - << " instead of " << quoted_base << ".\n"; - ost << "Running moc on\n" - << " " << quoted_header << "!\n" - << "Better include " << quoted_base - << " for compatibility with strict mode.\n" - "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n"; - this->LogFileWarning(cmQtAutoGen::MOC, absFilename, - ost.str()); - } - } else { - if (!ownDotMoc) { - // Handled further down - } - } - } + // 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::ostringstream ost; - ost << "The file includes the moc file " - << cmQtAutoGen::Quoted(incString) - << ", 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, ost.str()); - return false; + 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 { - // Mode: Strict - if (ownDotMoc) { - // Include self - fileToMoc = absFilename; - ownDotMocIncluded = true; - // Accept but issue a warning if moc isn't required - if (!selfRequiresMoc) { - std::ostringstream ost; - ost << "The file includes the moc file " - << cmQtAutoGen::Quoted(incString) - << ", but does not contain a " << this->MocMacroNamesString() - << " macro."; - this->LogFileWarning(cmQtAutoGen::MOC, absFilename, ost.str()); - } - } else { - // Don't allow FOO.moc include other than self in strict mode - std::ostringstream ost; - ost << "The file includes the moc file " - << cmQtAutoGen::Quoted(incString) - << ", which seems to be the moc file from a different " - "source file. This is not supported. Include " - << cmQtAutoGen::Quoted(scanFileBase + ".moc") - << " to run moc on this source file."; - this->LogFileError(cmQtAutoGen::MOC, absFilename, ost.str()); - return false; - } + 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; } - if (!fileToMoc.empty()) { - mocsIncluded[fileToMoc] = incString; - this->MocFindDepends(fileToMoc, contentText, mocDepends); + } + } 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; } - // Forward content pointer - contentChars += this->MocRegExpInclude.end(); } } if (selfRequiresMoc && !ownDotMocIncluded) { - // In this case, check whether the scanned file itself contains a - // Q_OBJECT. + // 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 (relaxed && !ownMocUnderscoreInclude.empty()) { + if (this->MocRelaxedMode && !ownMocUscInclude.empty()) { // This is for KDE4 compatibility: - std::ostringstream ost; - ost << "The file contains a " << selfMacroName - << " macro, but does not include " - << cmQtAutoGen::Quoted(scanFileBase + ".moc") - << ". Instead it includes " - << cmQtAutoGen::Quoted(ownMocUnderscoreInclude) << ".\n" - << "Running moc on\n" - << " " << cmQtAutoGen::Quoted(absFilename) << "!\n" - << "Better include " << cmQtAutoGen::Quoted(scanFileBase + ".moc") - << " for compatibility with strict mode.\n" - "(CMAKE_AUTOMOC_RELAXED_MODE warning)"; - this->LogFileWarning(cmQtAutoGen::MOC, absFilename, ost.str()); - - // Use scanned source file instead of scanned header file as moc source - mocsIncluded[absFilename] = ownMocUnderscoreInclude; - this->MocFindDepends(absFilename, contentText, mocDepends); - // Remove - mocsIncluded.erase(ownMocUnderscoreHeader); + 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::ostringstream ost; - ost << "The file contains a " << selfMacroName - << " macro, but does not include " - << cmQtAutoGen::Quoted(scanFileBase + ".moc") << "!\n" - << "Consider adding the include or enabling SKIP_AUTOMOC for this " - "file."; - this->LogFileError(cmQtAutoGen::MOC, absFilename, ost.str()); + 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 cmQtAutoGenerators::MocParseHeaderContent( - const std::string& absFilename, const std::string& contentText, - std::map<std::string, std::string>& mocsNotIncluded, - std::map<std::string, std::set<std::string>>& mocDepends) +void cmQtAutoGenerators::MocParseHeaderContent(std::string const& absFilename, + std::string const& contentText) { - // Log if (this->Verbose) { - this->LogInfo(cmQtAutoGen::MOC, "Checking " + absFilename); + this->LogInfo(cmQtAutoGen::MOC, "Checking: " + absFilename); } - if (this->MocRequired(contentText)) { - // Register moc job - mocsNotIncluded[absFilename] = - this->ChecksumedPath(absFilename, "moc_", this->ConfigSuffix + ".cpp"); - this->MocFindDepends(absFilename, contentText, mocDepends); - } -} -void cmQtAutoGenerators::SearchHeadersForSourceFile( - const std::string& absFilename, std::set<std::string>& mocHeaderFiles, - std::set<std::string>& uicHeaderFiles) const -{ - std::array<std::string, 2> basepaths; - { - std::string bpath = SubDirPrefix(absFilename); - bpath += cmSystemTools::GetFilenameWithoutLastExtension(absFilename); - // search for default header files and private header files - basepaths[0] = bpath; - basepaths[1] = bpath; - basepaths[1] += "_p"; - } + 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)) { + static std::string const prefix = "moc_"; + static std::string const suffix = this->ConfigSuffix + ".cpp"; - for (const std::string& bPath : basepaths) { - std::string headerName; - if (this->FindHeader(headerName, bPath)) { - // Moc headers - if (!this->MocSkip(absFilename) && !this->MocSkip(headerName)) { - mocHeaderFiles.insert(headerName); - } - // Uic headers - if (!this->UicSkip(absFilename) && !this->UicSkip(headerName)) { - uicHeaderFiles.insert(headerName); - } - } - } -} - -bool cmQtAutoGenerators::ParseHeaders( - const std::set<std::string>& mocHeaderFiles, - const std::set<std::string>& uicHeaderFiles, - const std::map<std::string, std::string>& mocsIncluded, - std::map<std::string, std::string>& mocsNotIncluded, - std::map<std::string, std::set<std::string>>& mocDepends, - std::map<std::string, std::vector<std::string>>& uisIncluded) -{ - bool success = true; - // Merged header files list to read files only once - std::set<std::string> headerFiles; - headerFiles.insert(mocHeaderFiles.begin(), mocHeaderFiles.end()); - headerFiles.insert(uicHeaderFiles.begin(), uicHeaderFiles.end()); - - for (std::string const& headerName : headerFiles) { - std::string contentText; - if (ReadAll(contentText, headerName)) { - // Parse header content for MOC - if ((mocHeaderFiles.find(headerName) != mocHeaderFiles.end()) && - (mocsIncluded.find(headerName) == mocsIncluded.end())) { - this->MocParseHeaderContent(headerName, contentText, mocsNotIncluded, - mocDepends); - } - // Parse header content for UIC - if (uicHeaderFiles.find(headerName) != uicHeaderFiles.end()) { - this->UicParseContent(headerName, contentText, uisIncluded); + auto job = cm::make_unique<MocJobAuto>(); + job->SourceFile = absFilename; + { + std::string& bld = job->BuildFileRel; + bld = this->FilePathChecksum.getPart(absFilename); + bld += "/"; + bld += prefix; + bld += cmSystemTools::GetFilenameWithoutLastExtension(absFilename); + bld += suffix; } - } else { - this->LogFileError(cmQtAutoGen::GEN, headerName, - "Could not read header file"); - success = false; - break; + this->MocFindDepends(absFilename, contentText, job->Depends); + this->MocJobsAuto.push_back(std::move(job)); } } - return success; } -bool cmQtAutoGenerators::MocGenerateAll( - const std::map<std::string, std::string>& mocsIncluded, - const std::map<std::string, std::string>& mocsNotIncluded, - const std::map<std::string, std::set<std::string>>& mocDepends) +bool cmQtAutoGenerators::MocGenerateAll() { if (!this->MocEnabled()) { return true; } - // Look for name collisions + // Look for name collisions in included moc files { - std::multimap<std::string, std::string> collisions; - // Test merged map of included and notIncluded - std::map<std::string, std::string> mergedMocs(mocsIncluded); - mergedMocs.insert(mocsNotIncluded.begin(), mocsNotIncluded.end()); - if (this->NameCollisionTest(mergedMocs, collisions)) { - std::string msg = "The same moc file will be generated " - "from different sources.\n" - "To avoid this error either\n" - " - rename the source files or\n" - " - do not include the (moc_NAME.cpp|NAME.moc) file"; - this->LogNameCollisionError(cmQtAutoGen::MOC, msg, collisions); + 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; } } - // Generate moc_predefs + // (Re)generate moc_predefs.h on demand if (!this->MocPredefsCmd.empty()) { if (this->MocSettingsChanged || - FileAbsentOrOlder(this->MocPredefsFileAbs, this->SettingsFile)) { + !cmSystemTools::FileExists(this->MocPredefsFileAbs)) { if (this->Verbose) { this->LogBold("Generating MOC predefs " + this->MocPredefsFileRel); } @@ -1216,16 +1371,9 @@ bool cmQtAutoGenerators::MocGenerateAll( { // 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); - } // Add options - cmd.insert(cmd.end(), this->MocOptions.begin(), - this->MocOptions.end()); + cmd.insert(cmd.end(), this->MocAllOptions.begin(), + this->MocAllOptions.end()); // Execute command if (!this->RunCommand(cmd, output)) { this->LogCommandError(cmQtAutoGen::MOC, @@ -1240,6 +1388,8 @@ bool cmQtAutoGenerators::MocGenerateAll( output)) { this->MocPredefsChanged = true; } else { + this->LogFileError(cmQtAutoGen::MOC, this->MocPredefsFileAbs, + "moc_predefs file writing failed"); return false; } } else { @@ -1254,101 +1404,90 @@ bool cmQtAutoGenerators::MocGenerateAll( } // Generate moc files that are included by source files. - for (auto const& it : mocsIncluded) { - if (!this->MocGenerateFile(it.first, it.second, mocDepends, true)) { - if (this->MocRunFailed) { - return false; - } + for (auto const& item : this->MocJobsIncluded) { + if (!this->MocGenerateFile(*item)) { + return false; } } - // Generate moc files that are _not_ included by source files. - bool mocCompFileGenerated = false; - for (auto const& it : mocsNotIncluded) { - if (this->MocGenerateFile(it.first, it.second, mocDepends, false)) { - mocCompFileGenerated = true; - } else { - if (this->MocRunFailed) { - return false; - } + bool autoNameGenerated = false; + for (auto const& item : this->MocJobsAuto) { + if (!this->MocGenerateFile(*item, &autoNameGenerated)) { + return false; } } // Compose mocs compilation file content - std::string automocSource; { - std::ostringstream ost; - ost << "/* This file is autogenerated, do not edit*/\n"; - if (mocsNotIncluded.empty()) { + std::string mocs = "/* This file is autogenerated, do not edit*/\n"; + if (this->MocJobsAuto.empty()) { // Dummy content - ost << "enum some_compilers { need_more_than_nothing };\n"; + mocs += "enum some_compilers { need_more_than_nothing };\n"; } else { // Valid content - for (auto const& it : mocsNotIncluded) { - ost << "#include \"" << it.second << "\"\n"; + for (const auto& item : this->MocJobsAuto) { + mocs += "#include \""; + mocs += item->BuildFileRel; + mocs += "\"\n"; } } - automocSource = ost.str(); - } - if (this->FileDiffers(this->MocCompFileAbs, automocSource)) { - // Actually write mocs compilation file - if (this->Verbose) { - this->LogBold("Generating MOC compilation " + this->MocCompFileRel); - } - if (!this->FileWrite(cmQtAutoGen::MOC, this->MocCompFileAbs, - automocSource)) { - return false; - } - } else if (mocCompFileGenerated) { - // Only touch mocs compilation file - if (this->Verbose) { - this->LogInfo(cmQtAutoGen::MOC, - "Touching mocs compilation " + this->MocCompFileRel); + if (this->FileDiffers(this->MocCompFileAbs, mocs)) { + // Actually write mocs compilation file + if (this->Verbose) { + 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->Verbose) { + this->LogInfo(cmQtAutoGen::MOC, + "Touching mocs compilation " + this->MocCompFileRel); + } + cmSystemTools::Touch(this->MocCompFileAbs, false); } - cmSystemTools::Touch(this->MocCompFileAbs, false); } return true; } /** - * @return True if a moc file was created. False may indicate an error. + * @return True on success */ -bool cmQtAutoGenerators::MocGenerateFile( - const std::string& sourceFile, const std::string& mocFileName, - const std::map<std::string, std::set<std::string>>& mocDepends, - bool included) +bool cmQtAutoGenerators::MocGenerateFile(const MocJobAuto& job, + bool* generated) { - bool mocGenerated = false; - bool generateMoc = this->MocSettingsChanged || this->MocPredefsChanged; + bool success = true; - const std::string mocFileRel = - included ? (this->AutogenIncludeDir + mocFileName) : mocFileName; - const std::string mocFileAbs = - cmSystemTools::CollapseCombinedPath(this->AutogenBuildDir, mocFileRel); + std::string const mocFileAbs = cmSystemTools::CollapseCombinedPath( + this->AutogenBuildDir, job.BuildFileRel); + bool generateMoc = (this->MocSettingsChanged || this->MocPredefsChanged); if (!generateMoc) { // Test if the source file is newer that the build file - generateMoc = FileAbsentOrOlder(mocFileAbs, sourceFile); - if (!generateMoc) { - // Test if a dependency file changed - std::map<std::string, std::set<std::string>>::const_iterator dit = - mocDepends.find(sourceFile); - if (dit != mocDepends.end()) { - for (std::string const& fit : dit->second) { - if (FileAbsentOrOlder(mocFileAbs, fit)) { - generateMoc = true; - break; - } - } + generateMoc = FileAbsentOrOlder(mocFileAbs, job.SourceFile); + } + if (!generateMoc && !this->MocPredefsFileAbs.empty()) { + // Check if moc_predefs is newer + generateMoc = FileAbsentOrOlder(mocFileAbs, this->MocPredefsFileAbs); + } + if (!generateMoc) { + // Test if a dependency file is newer + for (std::string const& depFile : job.Depends) { + if (FileAbsentOrOlder(mocFileAbs, depFile)) { + generateMoc = true; + break; } } } if (generateMoc) { // Log if (this->Verbose) { - this->LogBold("Generating MOC source " + mocFileRel); + this->LogBold("Generating MOC source " + job.BuildFileRel); } // Make sure the parent directory exists @@ -1356,15 +1495,9 @@ bool cmQtAutoGenerators::MocGenerateFile( // Compose moc command std::vector<std::string> cmd; cmd.push_back(this->MocExecutable); - // 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); - } // Add options - cmd.insert(cmd.end(), this->MocOptions.begin(), this->MocOptions.end()); + cmd.insert(cmd.end(), this->MocAllOptions.begin(), + this->MocAllOptions.end()); // Add predefs include if (!this->MocPredefsFileAbs.empty()) { cmd.push_back("--include"); @@ -1372,37 +1505,111 @@ bool cmQtAutoGenerators::MocGenerateFile( } cmd.push_back("-o"); cmd.push_back(mocFileAbs); - cmd.push_back(sourceFile); + cmd.push_back(job.SourceFile); // Execute moc command std::string output; if (this->RunCommand(cmd, output)) { // Success - mocGenerated = true; + if (generated != nullptr) { + *generated = true; + } } else { - this->LogCommandError(cmQtAutoGen::MOC, "moc failed for\n " + - cmQtAutoGen::Quoted(sourceFile), - cmd, output); + // Moc command failed + { + std::string emsg = "moc failed for\n "; + emsg += cmQtAutoGen::Quoted(job.SourceFile); + this->LogCommandError(cmQtAutoGen::MOC, emsg, cmd, output); + } cmSystemTools::RemoveFile(mocFileAbs); - this->MocRunFailed = true; + success = false; } } else { // Parent directory creation failed - this->MocRunFailed = true; + success = false; + } + } + return success; +} + +/** + * @brief Tests if the file name is in the skip list + */ +bool cmQtAutoGenerators::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 cmQtAutoGenerators::UicParseContent(std::string const& absFilename, + std::string const& contentText) +{ + if (this->Verbose) { + 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 mocGenerated; + + return true; } bool cmQtAutoGenerators::UicFindIncludedFile(std::string& absFile, - const std::string& sourceFile, - const std::string& searchPath, - const std::string& searchFile) + std::string const& sourceFile, + std::string const& includeString) { bool success = false; - std::vector<std::string> testFiles; + 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; @@ -1410,7 +1617,7 @@ bool cmQtAutoGenerators::UicFindIncludedFile(std::string& absFile, } // Vicinity of the source { - const std::string sourcePath = SubDirPrefix(sourceFile); + std::string const sourcePath = SubDirPrefix(sourceFile); testFiles.push_back(sourcePath + searchFile); if (!searchPath.empty()) { testFiles.push_back(sourcePath + searchFileFull); @@ -1440,76 +1647,75 @@ bool cmQtAutoGenerators::UicFindIncludedFile(std::string& absFile, // Log error if (!success) { - std::ostringstream ost; - ost << "Could not find " << cmQtAutoGen::Quoted(searchFile) << " in\n"; + std::string emsg = "Could not find "; + emsg += cmQtAutoGen::Quoted(searchFile); + emsg += " in\n"; for (std::string const& testFile : testFiles) { - ost << " " << cmQtAutoGen::Quoted(testFile) << "\n"; + emsg += " "; + emsg += cmQtAutoGen::Quoted(testFile); + emsg += "\n"; } - this->LogFileError(cmQtAutoGen::UIC, sourceFile, ost.str()); + this->LogFileError(cmQtAutoGen::UIC, sourceFile, emsg); } return success; } -bool cmQtAutoGenerators::UicGenerateAll( - const std::map<std::string, std::vector<std::string>>& uisIncluded) +bool cmQtAutoGenerators::UicGenerateAll() { if (!this->UicEnabled()) { return true; } - // single map with input / output names - std::map<std::string, std::map<std::string, std::string>> sourceGenMap; + // Look for name collisions in included uic files { - // Collision lookup map - std::map<std::string, std::string> testMap; - // Compile maps - for (auto const& sit : uisIncluded) { - const std::string& source(sit.first); - const std::vector<std::string>& sourceIncs(sit.second); - // insert new source/destination map - std::map<std::string, std::string>& uiGenMap = sourceGenMap[source]; - for (std::string const& inc : sourceIncs) { - // Remove ui_ from the begin filename by substr() - const std::string uiBasePath = SubDirPrefix(inc); - const std::string uiBaseName = - cmSystemTools::GetFilenameWithoutLastExtension(inc).substr(3); - const std::string uiFileName = uiBaseName + ".ui"; - std::string uiInputFile; - if (UicFindIncludedFile(uiInputFile, source, uiBasePath, uiFileName)) { - std::string uiOutputFile = uiBasePath; - uiOutputFile += "ui_"; - uiOutputFile += uiBaseName; - uiOutputFile += ".h"; - cmSystemTools::ReplaceString(uiOutputFile, "..", "__"); - uiGenMap[uiInputFile] = uiOutputFile; - testMap[uiInputFile] = uiOutputFile; - } else { - return false; - } + 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; } - } - // look for name collisions - { - std::multimap<std::string, std::string> collisions; - if (this->NameCollisionTest(testMap, collisions)) { - std::string msg = "The same ui_NAME.h file will be generated " - "from different sources.\n" - "To avoid this error rename the source files.\n"; - this->LogNameCollisionError(cmQtAutoGen::UIC, msg, collisions); - return false; + 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; } } - // generate ui files - for (auto const& srcItem : sourceGenMap) { - for (auto const& item : srcItem.second) { - if (!this->UicGenerateFile(srcItem.first, item.first, item.second)) { - if (this->UicRunFailed) { - return false; - } - } + // Generate ui header files + for (const auto& item : this->UicJobs) { + if (!this->UicGenerateFile(*item)) { + return false; } } @@ -1517,27 +1723,24 @@ bool cmQtAutoGenerators::UicGenerateAll( } /** - * @return True if a uic file was created. False may indicate an error. + * @return True on success */ -bool cmQtAutoGenerators::UicGenerateFile(const std::string& realName, - const std::string& uiInputFile, - const std::string& uiOutputFile) +bool cmQtAutoGenerators::UicGenerateFile(const UicJob& uicJob) { - bool uicGenerated = false; - bool generateUic = this->UicSettingsChanged; + bool success = true; - const std::string uicFileRel = this->AutogenIncludeDir + uiOutputFile; - const std::string uicFileAbs = - cmSystemTools::CollapseCombinedPath(this->AutogenBuildDir, uicFileRel); + std::string const uicFileAbs = cmSystemTools::CollapseCombinedPath( + this->AutogenBuildDir, uicJob.BuildFileRel); + bool generateUic = this->UicSettingsChanged; if (!generateUic) { // Test if the source file is newer that the build file - generateUic = FileAbsentOrOlder(uicFileAbs, uiInputFile); + generateUic = FileAbsentOrOlder(uicFileAbs, uicJob.SourceFile); } if (generateUic) { // Log if (this->Verbose) { - this->LogBold("Generating UIC header " + uicFileRel); + this->LogBold("Generating UIC header " + uicJob.BuildFileRel); } // Make sure the parent directory exists @@ -1547,7 +1750,7 @@ bool cmQtAutoGenerators::UicGenerateFile(const std::string& realName, cmd.push_back(this->UicExecutable); { std::vector<std::string> allOpts = this->UicTargetOptions; - auto optionIt = this->UicOptions.find(uiInputFile); + auto optionIt = this->UicOptions.find(uicJob.SourceFile); if (optionIt != this->UicOptions.end()) { cmQtAutoGen::UicMergeOptions(allOpts, optionIt->second, (this->QtMajorVersion == "5")); @@ -1556,31 +1759,29 @@ bool cmQtAutoGenerators::UicGenerateFile(const std::string& realName, } cmd.push_back("-o"); cmd.push_back(uicFileAbs); - cmd.push_back(uiInputFile); + cmd.push_back(uicJob.SourceFile); std::string output; if (this->RunCommand(cmd, output)) { // Success - uicGenerated = true; } else { // Command failed { - std::ostringstream ost; - ost << "uic failed for\n" - << " " << cmQtAutoGen::Quoted(uiInputFile) << "\n" - << "needed by\n" - << " " << cmQtAutoGen::Quoted(realName); - this->LogCommandError(cmQtAutoGen::UIC, ost.str(), cmd, output); + 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); } cmSystemTools::RemoveFile(uicFileAbs); - this->UicRunFailed = true; + success = false; } } else { // Parent directory creation failed - this->UicRunFailed = true; + success = false; } } - return uicGenerated; + return success; } bool cmQtAutoGenerators::RccGenerateAll() @@ -1589,24 +1790,22 @@ bool cmQtAutoGenerators::RccGenerateAll() return true; } - // Generate qrc files - for (RccJob const& rccJob : this->RccJobs) { + // Generate rcc files + for (const RccJob& rccJob : this->RccJobs) { if (!this->RccGenerateFile(rccJob)) { - if (this->RccRunFailed) { - return false; - } + return false; } } return true; } /** - * @return True if a rcc file was created. False may indicate an error. + * @return True on success */ bool cmQtAutoGenerators::RccGenerateFile(const RccJob& rccJob) { + bool success = true; bool rccGenerated = false; - bool generateRcc = this->RccSettingsChanged; std::string rccFileAbs; if (this->ConfigSuffix.empty()) { @@ -1618,37 +1817,37 @@ bool cmQtAutoGenerators::RccGenerateFile(const RccJob& rccJob) rccFileAbs += this->ConfigSuffix; rccFileAbs += cmSystemTools::GetFilenameLastExtension(rccJob.RccFile); } - const std::string rccFileRel = cmSystemTools::RelativePath( + std::string const rccFileRel = cmSystemTools::RelativePath( this->AutogenBuildDir.c_str(), rccFileAbs.c_str()); // Check if regeneration is required + bool generateRcc = this->RccSettingsChanged; if (!generateRcc) { // Test if the resources list file is newer than build file generateRcc = FileAbsentOrOlder(rccFileAbs, rccJob.QrcFile); - if (!generateRcc) { - // Acquire input file list - std::vector<std::string> readFiles; - const std::vector<std::string>* files = &rccJob.Inputs; - if (files->empty()) { - // Read input file list from qrc file - std::string error; - if (cmQtAutoGen::RccListInputs(this->QtMajorVersion, - this->RccExecutable, rccJob.QrcFile, - readFiles, &error)) { - files = &readFiles; - } else { - files = nullptr; - this->LogFileError(cmQtAutoGen::RCC, rccJob.QrcFile, error); - this->RccRunFailed = true; - } + } + if (!generateRcc) { + // Acquire input file list + std::vector<std::string> readFiles; + std::vector<std::string> const* files = &rccJob.Inputs; + if (rccJob.Inputs.empty()) { + // Read input file list from qrc file + std::string error; + if (cmQtAutoGen::RccListInputs(this->QtMajorVersion, this->RccExecutable, + rccJob.QrcFile, readFiles, &error)) { + files = &readFiles; + } else { + files = nullptr; + this->LogFileError(cmQtAutoGen::RCC, rccJob.QrcFile, error); + success = false; } - // Test if any input file is newer than the build file - if (files != nullptr) { - for (std::string const& file : *files) { - if (FileAbsentOrOlder(rccFileAbs, file)) { - generateRcc = true; - break; - } + } + // Test if any input file is newer than the build file + if (files != nullptr) { + for (std::string const& file : *files) { + if (FileAbsentOrOlder(rccFileAbs, file)) { + generateRcc = true; + break; } } } @@ -1675,22 +1874,24 @@ bool cmQtAutoGenerators::RccGenerateFile(const RccJob& rccJob) // Success rccGenerated = true; } else { - this->LogCommandError(cmQtAutoGen::RCC, "rcc failed for\n " + - cmQtAutoGen::Quoted(rccJob.QrcFile), - cmd, output); + { + std::string emsg = "rcc failed for\n "; + emsg += cmQtAutoGen::Quoted(rccJob.QrcFile); + this->LogCommandError(cmQtAutoGen::RCC, emsg, cmd, output); + } cmSystemTools::RemoveFile(rccFileAbs); - this->RccRunFailed = true; + success = false; } } else { // Parent directory creation failed - this->RccRunFailed = true; + success = false; } } // For a multi configuration generator generate a wrapper file - if (!this->ConfigSuffix.empty() && !this->RccRunFailed) { + if (success && !this->ConfigSuffix.empty()) { // Wrapper file name - const std::string& wrapperFileAbs = rccJob.RccFile; - const std::string wrapperFileRel = cmSystemTools::RelativePath( + std::string const& wrapperFileAbs = rccJob.RccFile; + std::string const wrapperFileRel = cmSystemTools::RelativePath( this->AutogenBuildDir.c_str(), wrapperFileAbs.c_str()); // Wrapper file content std::string content = "// This is an autogenerated configuration " @@ -1705,9 +1906,9 @@ bool cmQtAutoGenerators::RccGenerateFile(const RccJob& rccJob) this->LogBold("Generating RCC wrapper " + wrapperFileRel); } if (!this->FileWrite(cmQtAutoGen::RCC, wrapperFileAbs, content)) { - // Error - rccGenerated = false; - this->RccRunFailed = true; + this->LogFileError(cmQtAutoGen::RCC, wrapperFileAbs, + "rcc wrapper file writing failed"); + success = false; } } else if (rccGenerated) { // Only touch wrapper file if the content matches @@ -1719,10 +1920,10 @@ bool cmQtAutoGenerators::RccGenerateFile(const RccJob& rccJob) } } - return rccGenerated; + return success; } -void cmQtAutoGenerators::LogBold(const std::string& message) const +void cmQtAutoGenerators::LogBold(std::string const& message) const { cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundBlue | cmsysTerminal_Color_ForegroundBold, @@ -1730,7 +1931,7 @@ void cmQtAutoGenerators::LogBold(const std::string& message) const } void cmQtAutoGenerators::LogInfo(cmQtAutoGen::GeneratorType genType, - const std::string& message) const + std::string const& message) const { std::string msg = cmQtAutoGen::GeneratorName(genType); msg += ": "; @@ -1742,7 +1943,7 @@ void cmQtAutoGenerators::LogInfo(cmQtAutoGen::GeneratorType genType, } void cmQtAutoGenerators::LogWarning(cmQtAutoGen::GeneratorType genType, - const std::string& message) const + std::string const& message) const { std::string msg = cmQtAutoGen::GeneratorName(genType); msg += " warning:"; @@ -1758,182 +1959,102 @@ void cmQtAutoGenerators::LogWarning(cmQtAutoGen::GeneratorType genType, if (msg.back() != '\n') { msg.push_back('\n'); } - msg += "\n"; + msg.push_back('\n'); cmSystemTools::Stdout(msg.c_str(), msg.size()); } void cmQtAutoGenerators::LogFileWarning(cmQtAutoGen::GeneratorType genType, - const std::string& filename, - const std::string& message) const + std::string const& filename, + std::string const& message) const { - std::string emsg = " "; - emsg += cmQtAutoGen::Quoted(filename); - emsg += "\n"; + std::string msg = " "; + msg += cmQtAutoGen::Quoted(filename); + msg.push_back('\n'); // Message - emsg += message; - this->LogWarning(genType, emsg); + msg += message; + this->LogWarning(genType, msg); } void cmQtAutoGenerators::LogError(cmQtAutoGen::GeneratorType genType, - const std::string& message) const + std::string const& message) const { std::string msg; msg.push_back('\n'); - msg = cmQtAutoGen::GeneratorName(genType); - msg += " error:"; - if (message.find('\n') == std::string::npos) { - // Single line message - msg.push_back(' '); - } else { - // Multi line message - msg.push_back('\n'); - } + msg += HeadLine(cmQtAutoGen::GeneratorName(genType) + " error"); // Message msg += message; if (msg.back() != '\n') { msg.push_back('\n'); } - msg += "\n"; + msg.push_back('\n'); cmSystemTools::Stderr(msg.c_str(), msg.size()); } void cmQtAutoGenerators::LogFileError(cmQtAutoGen::GeneratorType genType, - const std::string& filename, - const std::string& message) const + std::string const& filename, + std::string const& message) const { std::string emsg = " "; emsg += cmQtAutoGen::Quoted(filename); - emsg += "\n"; + emsg += '\n'; // Message emsg += message; this->LogError(genType, emsg); } -void cmQtAutoGenerators::LogNameCollisionError( - cmQtAutoGen::GeneratorType genType, const std::string& message, - const std::multimap<std::string, std::string>& collisions) const -{ - std::string emsg; - // Add message - if (!message.empty()) { - emsg += message; - if (emsg.back() != '\n') { - emsg.push_back('\n'); - } - } - // Append collision list - for (auto const& item : collisions) { - emsg += " "; - emsg += item.first; - emsg += " -> "; - emsg += item.second; - emsg += "\n"; - } - this->LogError(genType, emsg); -} - void cmQtAutoGenerators::LogCommandError( - cmQtAutoGen::GeneratorType genType, const std::string& message, - const std::vector<std::string>& command, const std::string& output) const + cmQtAutoGen::GeneratorType genType, std::string const& message, + std::vector<std::string> const& command, std::string const& output) const { std::string msg; msg.push_back('\n'); - msg += cmQtAutoGen::GeneratorName(genType); - msg += " subprocess error: "; + msg += HeadLine(cmQtAutoGen::GeneratorName(genType) + " subprocess error"); msg += message; if (msg.back() != '\n') { msg.push_back('\n'); } msg.push_back('\n'); - msg += "Command\n"; - msg += "-------\n"; + msg += HeadLine("Command"); msg += QuotedCommand(command); if (msg.back() != '\n') { msg.push_back('\n'); } msg.push_back('\n'); - msg += "Output\n"; - msg += "------\n"; + msg += HeadLine("Output"); msg += output; if (msg.back() != '\n') { msg.push_back('\n'); } - msg += "\n"; + msg.push_back('\n'); cmSystemTools::Stderr(msg.c_str(), msg.size()); } /** - * @brief Collects name collisions as output/input pairs - * @return True if there were collisions - */ -bool cmQtAutoGenerators::NameCollisionTest( - const std::map<std::string, std::string>& genFiles, - std::multimap<std::string, std::string>& collisions) const -{ - typedef std::map<std::string, std::string>::const_iterator Iter; - typedef std::map<std::string, std::string>::value_type VType; - for (Iter ait = genFiles.begin(); ait != genFiles.end(); ++ait) { - bool first_match(true); - for (Iter bit = (++Iter(ait)); bit != genFiles.end(); ++bit) { - if (ait->second == bit->second) { - if (first_match) { - if (collisions.find(ait->second) != collisions.end()) { - // We already know of this collision from before - break; - } - collisions.insert(VType(ait->second, ait->first)); - first_match = false; - } - collisions.insert(VType(bit->second, bit->first)); - } - } - } - - return !collisions.empty(); -} - -/** - * @brief Generates a file path based on the checksum of the source file path - * @return The path - */ -std::string cmQtAutoGenerators::ChecksumedPath( - const std::string& sourceFile, const std::string& basePrefix, - const std::string& baseSuffix) const -{ - std::string res = FPathChecksum.getPart(sourceFile); - res += "/"; - res += basePrefix; - res += cmSystemTools::GetFilenameWithoutLastExtension(sourceFile); - res += baseSuffix; - return res; -} - -/** * @brief Generates the parent directory of the given file on demand * @return True on success */ bool cmQtAutoGenerators::MakeParentDirectory( - cmQtAutoGen::GeneratorType genType, const std::string& filename) const + cmQtAutoGen::GeneratorType genType, std::string const& filename) const { bool success = true; - const std::string dirName = cmSystemTools::GetFilenamePath(filename); + std::string const dirName = cmSystemTools::GetFilenamePath(filename); if (!dirName.empty()) { - success = cmSystemTools::MakeDirectory(dirName); - if (!success) { + if (!cmSystemTools::MakeDirectory(dirName)) { this->LogFileError(genType, filename, "Could not create parent directory"); + success = false; } } return success; } -bool cmQtAutoGenerators::FileDiffers(const std::string& filename, - const std::string& content) +bool cmQtAutoGenerators::FileDiffers(std::string const& filename, + std::string const& content) { bool differs = true; { std::string oldContents; - if (ReadAll(oldContents, filename)) { + if (ReadFile(oldContents, filename)) { differs = (oldContents != content); } } @@ -1941,14 +2062,15 @@ bool cmQtAutoGenerators::FileDiffers(const std::string& filename, } bool cmQtAutoGenerators::FileWrite(cmQtAutoGen::GeneratorType genType, - const std::string& filename, - const std::string& content) + std::string const& filename, + std::string const& content) { std::string error; // Make sure the parent directory exists if (this->MakeParentDirectory(genType, filename)) { cmsys::ofstream outfile; - outfile.open(filename.c_str(), std::ios::trunc); + outfile.open(filename.c_str(), + (std::ios::out | std::ios::binary | std::ios::trunc)); if (outfile) { outfile << content; // Check for write errors @@ -1970,7 +2092,7 @@ bool cmQtAutoGenerators::FileWrite(cmQtAutoGen::GeneratorType genType, * @brief Runs a command and returns true on success * @return True on success */ -bool cmQtAutoGenerators::RunCommand(const std::vector<std::string>& command, +bool cmQtAutoGenerators::RunCommand(std::vector<std::string> const& command, std::string& output) const { // Log command @@ -1992,7 +2114,7 @@ bool cmQtAutoGenerators::RunCommand(const std::vector<std::string>& command, * @return True on success */ bool cmQtAutoGenerators::FindHeader(std::string& header, - const std::string& testBasePath) const + std::string const& testBasePath) const { for (std::string const& ext : this->HeaderExtensions) { std::string testFilePath(testBasePath); @@ -2005,56 +2127,3 @@ bool cmQtAutoGenerators::FindHeader(std::string& header, } return false; } - -std::string cmQtAutoGenerators::MocFindHeader( - const std::string& sourcePath, const std::string& 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 (FindHeader(header, fullPath)) { - break; - } - } - } - // Sanitize - if (!header.empty()) { - header = cmSystemTools::GetRealPath(header); - } - return header; -} - -bool cmQtAutoGenerators::MocFindIncludedFile( - std::string& absFile, const std::string& sourcePath, - const std::string& 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; -} diff --git a/Source/cmQtAutoGenerators.h b/Source/cmQtAutoGenerators.h index 5d3ad74..aeffbb1 100644 --- a/Source/cmQtAutoGenerators.h +++ b/Source/cmQtAutoGenerators.h @@ -10,6 +10,7 @@ #include "cmsys/RegularExpression.hxx" #include <map> +#include <memory> // IWYU pragma: keep #include <set> #include <string> #include <vector> @@ -21,7 +22,7 @@ class cmQtAutoGenerators CM_DISABLE_COPY(cmQtAutoGenerators) public: cmQtAutoGenerators(); - bool Run(const std::string& targetDirectory, const std::string& config); + bool Run(std::string const& targetDirectory, std::string const& config); private: // -- Types @@ -37,7 +38,7 @@ private: { } - KeyRegExp(const std::string& key, const std::string& regExp) + KeyRegExp(std::string const& key, std::string const& regExp) : Key(key) , RegExp(regExp) { @@ -47,6 +48,38 @@ private: cmsys::RegularExpression RegExp; }; + /// @brief Source file job + struct SourceJob + { + bool Moc = false; + bool Uic = false; + }; + + /// @brief MOC job + struct MocJobAuto + { + std::string SourceFile; + std::string BuildFileRel; + std::set<std::string> Depends; + }; + + /// @brief MOC job + struct MocJobIncluded : MocJobAuto + { + bool DependsValid = false; + std::string Includer; + std::string IncludeString; + }; + + /// @brief UIC job + struct UicJob + { + std::string SourceFile; + std::string BuildFileRel; + std::string Includer; + std::string IncludeString; + }; + /// @brief RCC job struct RccJob { @@ -56,149 +89,110 @@ private: std::vector<std::string> Inputs; }; - // -- Configuration - bool MocDependFilterPush(const std::string& key, const std::string& regExp); - bool ReadAutogenInfoFile(cmMakefile* makefile, - const std::string& targetDirectory, - const std::string& config); - - bool MocEnabled() const { return !this->MocExecutable.empty(); } - bool UicEnabled() const { return !this->UicExecutable.empty(); } - bool RccEnabled() const { return !this->RccExecutable.empty(); } + // -- Initialization + bool InitInfoFile(cmMakefile* makefile, std::string const& targetDirectory, + std::string const& config); // -- Settings file void SettingsFileRead(cmMakefile* makefile); bool SettingsFileWrite(); - - bool AnySettingsChanged() const + bool SettingsChanged() const { return (this->MocSettingsChanged || this->RccSettingsChanged || this->UicSettingsChanged); } - // -- Init and run - void Init(cmMakefile* makefile); - bool RunAutogen(); - - // -- Content analysis - bool MocRequired(const std::string& contentText, - std::string* macroName = nullptr); - void MocFindDepends( - const std::string& absFilename, const std::string& contentText, - std::map<std::string, std::set<std::string>>& mocDepends); - - bool MocSkip(const std::string& absFilename) const; - bool UicSkip(const std::string& absFilename) const; - - bool ParseSourceFile( - const std::string& absFilename, - std::map<std::string, std::string>& mocsIncluded, - std::map<std::string, std::set<std::string>>& mocDepends, - std::map<std::string, std::vector<std::string>>& includedUis, - bool relaxed); - - void SearchHeadersForSourceFile(const std::string& absFilename, - std::set<std::string>& mocHeaderFiles, - std::set<std::string>& uicHeaderFiles) const; - - bool ParseHeaders( - const std::set<std::string>& mocHeaderFiles, - const std::set<std::string>& uicHeaderFiles, - const std::map<std::string, std::string>& mocsIncluded, - std::map<std::string, std::string>& mocsNotIncluded, - std::map<std::string, std::set<std::string>>& mocDepends, - std::map<std::string, std::vector<std::string>>& includedUis); - - void UicParseContent( - const std::string& fileName, const std::string& contentText, - std::map<std::string, std::vector<std::string>>& includedUis); - - std::string MocMacroNamesString() const; - std::string MocHeaderSuffixesString() const; - - bool MocParseSourceContent( - const std::string& absFilename, const std::string& contentText, - std::map<std::string, std::string>& mocsIncluded, - std::map<std::string, std::set<std::string>>& mocDepends, bool relaxed); + // -- Central processing + bool Process(); - void MocParseHeaderContent( - const std::string& absFilename, const std::string& contentText, - std::map<std::string, std::string>& mocsNotIncluded, - std::map<std::string, std::set<std::string>>& mocDepends); + // -- Source parsing + bool ParseSourceFile(std::string const& absFilename, const SourceJob& job); + bool ParseHeaderFile(std::string const& absFilename, const SourceJob& job); + bool ParsePostprocess(); - // -- Moc file generation - bool MocGenerateAll( - const std::map<std::string, std::string>& mocsIncluded, - const std::map<std::string, std::string>& mocsNotIncluded, - const std::map<std::string, std::set<std::string>>& mocDepends); - bool MocGenerateFile( - const std::string& sourceFile, const std::string& mocFileName, - const std::map<std::string, std::set<std::string>>& mocDepends, - bool included); + // -- Moc + bool MocEnabled() const { return !this->MocExecutable.empty(); } + bool MocSkip(std::string const& absFilename) const; + bool MocRequired(std::string const& contentText, + std::string* macroName = nullptr); + // Moc strings + std::string MocStringMacros() const; + std::string MocStringHeaders(std::string const& fileBase) const; + std::string MocFindIncludedHeader(std::string const& sourcePath, + std::string const& includeBase) const; + bool MocFindIncludedFile(std::string& absFile, std::string const& sourceFile, + std::string const& includeString) const; + // Moc depends + bool MocDependFilterPush(std::string const& key, std::string const& regExp); + void MocFindDepends(std::string const& absFilename, + std::string const& contentText, + std::set<std::string>& depends); + // Moc + bool MocParseSourceContent(std::string const& absFilename, + std::string const& contentText); + void MocParseHeaderContent(std::string const& absFilename, + std::string const& contentText); + + bool MocGenerateAll(); + bool MocGenerateFile(const MocJobAuto& job, bool* generated = nullptr); - // -- Uic file generation - bool UicFindIncludedFile(std::string& absFile, const std::string& sourceFile, - const std::string& searchPath, - const std::string& searchFile); - bool UicGenerateAll( - const std::map<std::string, std::vector<std::string>>& includedUis); - bool UicGenerateFile(const std::string& realName, - const std::string& uiInputFile, - const std::string& uiOutputFile); + // -- 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); - // -- Rcc file generation + // -- Rcc + bool RccEnabled() const { return !this->RccExecutable.empty(); } bool RccGenerateAll(); bool RccGenerateFile(const RccJob& rccJob); // -- Log info - void LogBold(const std::string& message) const; + void LogBold(std::string const& message) const; void LogInfo(cmQtAutoGen::GeneratorType genType, - const std::string& message) const; + std::string const& message) const; // -- Log warning void LogWarning(cmQtAutoGen::GeneratorType genType, - const std::string& message) const; + std::string const& message) const; void LogFileWarning(cmQtAutoGen::GeneratorType genType, - const std::string& filename, - const std::string& message) const; + std::string const& filename, + std::string const& message) const; // -- Log error void LogError(cmQtAutoGen::GeneratorType genType, - const std::string& message) const; + std::string const& message) const; void LogFileError(cmQtAutoGen::GeneratorType genType, - const std::string& filename, - const std::string& message) const; + std::string const& filename, + std::string const& message) const; void LogCommandError(cmQtAutoGen::GeneratorType genType, - const std::string& message, - const std::vector<std::string>& command, - const std::string& output) const; - void LogNameCollisionError( - cmQtAutoGen::GeneratorType genType, const std::string& message, - const std::multimap<std::string, std::string>& collisions) const; + std::string const& message, + std::vector<std::string> const& command, + std::string const& output) const; // -- Utility - bool NameCollisionTest( - const std::map<std::string, std::string>& genFiles, - std::multimap<std::string, std::string>& collisions) const; - std::string ChecksumedPath(const std::string& sourceFile, - const std::string& basePrefix, - const std::string& baseSuffix) const; bool MakeParentDirectory(cmQtAutoGen::GeneratorType genType, - const std::string& filename) const; - bool FileDiffers(const std::string& filename, const std::string& content); + std::string const& filename) const; + bool FileDiffers(std::string const& filename, std::string const& content); bool FileWrite(cmQtAutoGen::GeneratorType genType, - const std::string& filename, const std::string& content); - - bool RunCommand(const std::vector<std::string>& command, + std::string const& filename, std::string const& content); + bool FindHeader(std::string& header, std::string const& testBasePath) const; + bool RunCommand(std::vector<std::string> const& command, std::string& output) const; - bool FindHeader(std::string& header, const std::string& testBasePath) const; - - std::string MocFindHeader(const std::string& sourcePath, - const std::string& includeBase) const; - bool MocFindIncludedFile(std::string& absFile, const std::string& sourceFile, - const std::string& includeString) const; - // -- Meta std::string ConfigSuffix; + std::string InfoFile; + // -- Settings + bool IncludeProjectDirsBefore; + bool Verbose; + bool ColorOutput; + std::string SettingsFile; + std::string SettingsStringMoc; + std::string SettingsStringUic; + std::string SettingsStringRcc; // -- Directories std::string ProjectSourceDir; std::string ProjectBinaryDir; @@ -213,23 +207,14 @@ private: std::string UicExecutable; std::string RccExecutable; // -- File lists - std::vector<std::string> Sources; - std::vector<std::string> Headers; + std::map<std::string, SourceJob> HeaderJobs; + std::map<std::string, SourceJob> SourceJobs; std::vector<std::string> HeaderExtensions; - cmFilePathChecksum FPathChecksum; - // -- Settings - bool IncludeProjectDirsBefore; - bool Verbose; - bool ColorOutput; - std::string SettingsFile; - std::string SettingsStringMoc; - std::string SettingsStringUic; - std::string SettingsStringRcc; + cmFilePathChecksum FilePathChecksum; // -- Moc bool MocSettingsChanged; bool MocPredefsChanged; bool MocRelaxedMode; - bool MocRunFailed; std::string MocCompFileRel; std::string MocCompFileAbs; std::string MocPredefsFileRel; @@ -239,21 +224,23 @@ private: 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; - bool UicRunFailed; 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; // -- Rcc bool RccSettingsChanged; - bool RccRunFailed; std::vector<RccJob> RccJobs; }; |