From 27ed3b3537676e6090a0845e4805bb4a65d05bae Mon Sep 17 00:00:00 2001 From: Sebastian Holtermann Date: Fri, 17 Nov 2017 13:04:26 +0100 Subject: Autogen: Rename cmQtAutoGenerators to cmQtAutoGeneratorMocUic --- Source/CMakeLists.txt | 4 +- Source/cmQtAutoGeneratorMocUic.cxx | 2045 ++++++++++++++++++++++++++++++++++++ Source/cmQtAutoGeneratorMocUic.h | 228 ++++ Source/cmQtAutoGenerators.cxx | 2045 ------------------------------------ Source/cmQtAutoGenerators.h | 228 ---- Source/cmcmd.cxx | 4 +- 6 files changed, 2277 insertions(+), 2277 deletions(-) create mode 100644 Source/cmQtAutoGeneratorMocUic.cxx create mode 100644 Source/cmQtAutoGeneratorMocUic.h delete mode 100644 Source/cmQtAutoGenerators.cxx delete mode 100644 Source/cmQtAutoGenerators.h diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index fd02196..cddef68 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -314,8 +314,8 @@ set(SRCS cmQtAutoGenDigest.h cmQtAutoGeneratorInitializer.cxx cmQtAutoGeneratorInitializer.h - cmQtAutoGenerators.cxx - cmQtAutoGenerators.h + cmQtAutoGeneratorMocUic.cxx + cmQtAutoGeneratorMocUic.h cmQtAutoGeneratorRcc.cxx cmQtAutoGeneratorRcc.h cmRST.cxx diff --git a/Source/cmQtAutoGeneratorMocUic.cxx b/Source/cmQtAutoGeneratorMocUic.cxx new file mode 100644 index 0000000..36fc090 --- /dev/null +++ b/Source/cmQtAutoGeneratorMocUic.cxx @@ -0,0 +1,2045 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmQtAutoGen.h" +#include "cmQtAutoGeneratorMocUic.h" + +#include "cmsys/FStream.hxx" +#include "cmsys/Terminal.h" +#include +#include +#include +#include +#include +#include +#include + +#include "cmAlgorithms.h" +#include "cmCryptoHash.h" +#include "cmFilePathChecksum.h" +#include "cmGlobalGenerator.h" +#include "cmMakefile.h" +#include "cmOutputConverter.h" +#include "cmStateDirectory.h" +#include "cmStateSnapshot.h" +#include "cmSystemTools.h" +#include "cmake.h" + +#if defined(__APPLE__) +#include +#endif + +// -- Static variables + +static const char* SettingsKeyMoc = "AM_MOC_SETTINGS_HASH"; +static const char* SettingsKeyUic = "AM_UIC_SETTINGS_HASH"; + +// -- Static functions + +static std::string HeadLine(std::string const& title) +{ + std::string head = title; + head += '\n'; + head.append(head.size() - 1, '-'); + head += '\n'; + return head; +} + +static std::string QuotedCommand(std::vector const& command) +{ + std::string res; + for (std::string const& item : command) { + if (!res.empty()) { + res.push_back(' '); + } + std::string const cesc = cmQtAutoGen::Quoted(item); + if (item.empty() || (cesc.size() > (item.size() + 2)) || + (cesc.find(' ') != std::string::npos)) { + res += cesc; + } else { + res += item; + } + } + return res; +} + +static std::string SubDirPrefix(std::string const& fileName) +{ + std::string res(cmSystemTools::GetFilenamePath(fileName)); + if (!res.empty()) { + res += '/'; + } + return res; +} + +static bool ReadFile(std::string& content, std::string const& filename, + std::string* error = nullptr) +{ + bool success = false; + if (cmSystemTools::FileExists(filename)) { + std::size_t const length = cmSystemTools::FileLength(filename); + cmsys::ifstream ifs(filename.c_str(), (std::ios::in | std::ios::binary)); + if (ifs) { + content.resize(length); + ifs.read(&content.front(), content.size()); + 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; +} + +/** + * @brief Tests if buildFile is older than sourceFile + * @return True if buildFile is older than sourceFile. + * False may indicate an error. + */ +static bool FileIsOlderThan(std::string const& buildFile, + std::string const& sourceFile, + std::string* error = nullptr) +{ + int result = 0; + if (cmSystemTools::FileTimeCompare(buildFile, sourceFile, &result)) { + return (result < 0); + } + if (error != nullptr) { + error->append( + "File modification time comparison failed for the files\n "); + error->append(cmQtAutoGen::Quoted(buildFile)); + error->append("\nand\n "); + error->append(cmQtAutoGen::Quoted(sourceFile)); + } + return false; +} + +static bool ListContains(std::vector const& list, + std::string const& entry) +{ + return (std::find(list.begin(), list.end(), entry) != list.end()); +} + +// -- Class methods + +cmQtAutoGeneratorMocUic::cmQtAutoGeneratorMocUic() + : MultiConfig(cmQtAutoGen::WRAP) + , IncludeProjectDirsBefore(false) + , Verbose(cmSystemTools::HasEnv("VERBOSE")) + , ColorOutput(true) + , MocSettingsChanged(false) + , MocPredefsChanged(false) + , MocRelaxedMode(false) + , UicSettingsChanged(false) +{ + { + std::string colorEnv; + cmSystemTools::GetEnv("COLOR", colorEnv); + if (!colorEnv.empty()) { + this->ColorOutput = cmSystemTools::IsOn(colorEnv.c_str()); + } + } + + // Precompile regular expressions + this->MocRegExpInclude.compile( + "[\n][ \t]*#[ \t]*include[ \t]+" + "[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]"); + this->UicRegExpInclude.compile("[\n][ \t]*#[ \t]*include[ \t]+" + "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]"); +} + +bool cmQtAutoGeneratorMocUic::Run(std::string const& targetDirectory, + std::string const& config) +{ + cmake cm(cmake::RoleScript); + cm.SetHomeOutputDirectory(targetDirectory); + cm.SetHomeDirectory(targetDirectory); + cm.GetCurrentSnapshot().SetDefaultDefinitions(); + cmGlobalGenerator gg(&cm); + + cmStateSnapshot snapshot = cm.GetCurrentSnapshot(); + snapshot.GetDirectory().SetCurrentBinary(targetDirectory); + snapshot.GetDirectory().SetCurrentSource(targetDirectory); + + auto makefile = cm::make_unique(&gg, snapshot); + gg.SetCurrentMakefile(makefile.get()); + + bool success = false; + if (this->InitInfoFile(makefile.get(), targetDirectory, config)) { + // Read latest settings + this->SettingsFileRead(makefile.get()); + if (this->Process()) { + // Write current settings + if (this->SettingsFileWrite()) { + success = true; + } + } + } + return success; +} + +bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile, + std::string const& targetDirectory, + std::string const& config) +{ + // -- Meta + this->HeaderExtensions = makefile->GetCMakeInstance()->GetHeaderExtensions(); + + // Utility lambdas + auto InfoGet = [makefile](const char* key) { + return makefile->GetSafeDefinition(key); + }; + auto InfoGetBool = [makefile](const char* key) { + return makefile->IsOn(key); + }; + auto InfoGetList = [makefile](const char* key) -> std::vector { + std::vector list; + cmSystemTools::ExpandListArgument(makefile->GetSafeDefinition(key), list); + return list; + }; + auto InfoGetLists = + [makefile](const char* key) -> std::vector> { + std::vector> lists; + { + std::string const value = makefile->GetSafeDefinition(key); + std::string::size_type pos = 0; + while (pos < value.size()) { + std::string::size_type next = value.find(cmQtAutoGen::listSep, pos); + std::string::size_type length = + (next != std::string::npos) ? next - pos : value.size() - pos; + // Remove enclosing braces + if (length >= 2) { + std::string::const_iterator itBeg = value.begin() + (pos + 1); + std::string::const_iterator itEnd = itBeg + (length - 2); + { + std::string subValue(itBeg, itEnd); + std::vector list; + cmSystemTools::ExpandListArgument(subValue, list); + lists.push_back(std::move(list)); + } + } + pos += length; + pos += cmQtAutoGen::listSep.size(); + } + } + return lists; + }; + auto InfoGetConfig = [makefile, &config](const char* key) -> std::string { + const char* valueConf = nullptr; + { + std::string keyConf = key; + keyConf += '_'; + keyConf += config; + valueConf = makefile->GetDefinition(keyConf); + } + if (valueConf == nullptr) { + valueConf = makefile->GetSafeDefinition(key); + } + return std::string(valueConf); + }; + auto InfoGetConfigList = + [&InfoGetConfig](const char* key) -> std::vector { + std::vector list; + cmSystemTools::ExpandListArgument(InfoGetConfig(key), list); + return list; + }; + + // -- Read info file + this->InfoFile = cmSystemTools::CollapseFullPath(targetDirectory); + cmSystemTools::ConvertToUnixSlashes(this->InfoFile); + this->InfoFile += "/AutogenInfo.cmake"; + if (!makefile->ReadListFile(this->InfoFile.c_str())) { + this->LogFileError(cmQtAutoGen::GEN, this->InfoFile, + "File processing failed"); + return false; + } + + // -- Meta + this->MultiConfig = cmQtAutoGen::MultiConfigType(InfoGet("AM_MULTI_CONFIG")); + this->ConfigSuffix = InfoGetConfig("AM_CONFIG_SUFFIX"); + if (this->ConfigSuffix.empty()) { + this->ConfigSuffix = "_"; + this->ConfigSuffix += config; + } + + // - Files and directories + this->ProjectSourceDir = InfoGet("AM_CMAKE_SOURCE_DIR"); + this->ProjectBinaryDir = InfoGet("AM_CMAKE_BINARY_DIR"); + this->CurrentSourceDir = InfoGet("AM_CMAKE_CURRENT_SOURCE_DIR"); + this->CurrentBinaryDir = InfoGet("AM_CMAKE_CURRENT_BINARY_DIR"); + this->IncludeProjectDirsBefore = + InfoGetBool("AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE"); + this->AutogenBuildDir = InfoGet("AM_BUILD_DIR"); + if (this->AutogenBuildDir.empty()) { + this->LogFileError(cmQtAutoGen::GEN, this->InfoFile, + "Autogen build directory missing"); + return false; + } + + // - Qt environment + this->QtMajorVersion = InfoGet("AM_QT_VERSION_MAJOR"); + this->QtMinorVersion = InfoGet("AM_QT_VERSION_MINOR"); + this->MocExecutable = InfoGet("AM_QT_MOC_EXECUTABLE"); + this->UicExecutable = InfoGet("AM_QT_UIC_EXECUTABLE"); + + // Check Qt version + if ((this->QtMajorVersion != "4") && (this->QtMajorVersion != "5")) { + this->LogFileError(cmQtAutoGen::GEN, this->InfoFile, + "Unsupported Qt version: " + + cmQtAutoGen::Quoted(this->QtMajorVersion)); + return false; + } + + // - Moc + if (this->MocEnabled()) { + this->MocSkipList = InfoGetList("AM_MOC_SKIP"); + this->MocDefinitions = InfoGetConfigList("AM_MOC_DEFINITIONS"); +#ifdef _WIN32 + { + std::string const win32("WIN32"); + if (!ListContains(this->MocDefinitions, win32)) { + this->MocDefinitions.push_back(win32); + } + } +#endif + this->MocIncludePaths = InfoGetConfigList("AM_MOC_INCLUDES"); + this->MocOptions = InfoGetList("AM_MOC_OPTIONS"); + this->MocRelaxedMode = InfoGetBool("AM_MOC_RELAXED_MODE"); + { + std::vector const MocMacroNames = + InfoGetList("AM_MOC_MACRO_NAMES"); + for (std::string const& item : MocMacroNames) { + this->MocMacroFilters.emplace_back( + item, ("[\n][ \t]*{?[ \t]*" + item).append("[^a-zA-Z0-9_]")); + } + } + { + std::vector const mocDependFilters = + InfoGetList("AM_MOC_DEPEND_FILTERS"); + // Insert Q_PLUGIN_METADATA dependency filter + if (this->QtMajorVersion != "4") { + this->MocDependFilterPush("Q_PLUGIN_METADATA", + "[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\(" + "[^\\)]*FILE[ \t]*\"([^\"]+)\""); + } + // Insert user defined dependency filters + if ((mocDependFilters.size() % 2) == 0) { + for (std::vector::const_iterator + dit = mocDependFilters.begin(), + ditEnd = mocDependFilters.end(); + dit != ditEnd; dit += 2) { + if (!this->MocDependFilterPush(*dit, *(dit + 1))) { + return false; + } + } + } else { + this->LogFileError( + cmQtAutoGen::MOC, this->InfoFile, + "AUTOMOC_DEPEND_FILTERS list size is not a multiple of 2"); + return false; + } + } + this->MocPredefsCmd = InfoGetList("AM_MOC_PREDEFS_CMD"); + } + + // - Uic + if (this->UicEnabled()) { + this->UicSkipList = InfoGetList("AM_UIC_SKIP"); + this->UicSearchPaths = InfoGetList("AM_UIC_SEARCH_PATHS"); + this->UicTargetOptions = InfoGetConfigList("AM_UIC_TARGET_OPTIONS"); + { + auto sources = InfoGetList("AM_UIC_OPTIONS_FILES"); + auto options = InfoGetLists("AM_UIC_OPTIONS_OPTIONS"); + // Compare list sizes + if (sources.size() != options.size()) { + std::ostringstream ost; + ost << "files/options lists sizes missmatch (" << sources.size() << "/" + << options.size() << ")"; + 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; + } + } + } + + // Initialize source file jobs + { + // Utility lambdas + auto AddJob = [this](std::map& 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 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 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"; + if (this->MultiConfig != cmQtAutoGen::SINGLE) { + this->AutogenIncludeDir += this->ConfigSuffix; + } + this->AutogenIncludeDir += "/"; + + // Moc variables + if (this->MocEnabled()) { + // Mocs compilation file + this->MocCompFileRel = "mocs_compilation"; + if (this->MultiConfig == cmQtAutoGen::FULL) { + this->MocCompFileRel += this->ConfigSuffix; + } + this->MocCompFileRel += ".cpp"; + this->MocCompFileAbs = cmSystemTools::CollapseCombinedPath( + this->AutogenBuildDir, this->MocCompFileRel); + + // Moc predefs file + if (!this->MocPredefsCmd.empty()) { + this->MocPredefsFileRel = "moc_predefs"; + if (this->MultiConfig != cmQtAutoGen::SINGLE) { + 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 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 const movePaths = { + { &this->ProjectBinaryDir, &this->ProjectSourceDir } + }; + for (std::string const* ppath : movePaths) { + std::list::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 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 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()); + } + } + + // - Old settings file + { + this->SettingsFile = cmSystemTools::CollapseFullPath(targetDirectory); + cmSystemTools::ConvertToUnixSlashes(this->SettingsFile); + this->SettingsFile += "/AutogenOldSettings"; + if (this->MultiConfig != cmQtAutoGen::SINGLE) { + this->SettingsFile += this->ConfigSuffix; + } + this->SettingsFile += ".cmake"; + } + + return true; +} + +void cmQtAutoGeneratorMocUic::SettingsFileRead(cmMakefile* makefile) +{ + // Compose current settings strings + { + cmCryptoHash crypt(cmCryptoHash::AlgoSHA256); + std::string const sep(" ~~~ "); + if (this->MocEnabled()) { + std::string str; + str += this->MocExecutable; + str += sep; + str += cmJoin(this->MocAllOptions, ";"); + str += sep; + str += this->IncludeProjectDirsBefore ? "TRUE" : "FALSE"; + str += sep; + str += cmJoin(this->MocPredefsCmd, ";"); + str += sep; + this->SettingsStringMoc = crypt.HashString(str); + } + if (this->UicEnabled()) { + std::string str; + str += this->UicExecutable; + str += sep; + str += cmJoin(this->UicTargetOptions, ";"); + for (const auto& item : this->UicOptions) { + str += sep; + str += item.first; + str += sep; + str += cmJoin(item.second, ";"); + } + str += sep; + this->SettingsStringUic = crypt.HashString(str); + } + } + + // Read old settings + if (makefile->ReadListFile(this->SettingsFile.c_str())) { + { + auto SMatch = [makefile](const char* key, std::string const& value) { + return (value == makefile->GetSafeDefinition(key)); + }; + if (!SMatch(SettingsKeyMoc, this->SettingsStringMoc)) { + this->MocSettingsChanged = true; + } + if (!SMatch(SettingsKeyUic, this->SettingsStringUic)) { + this->UicSettingsChanged = true; + } + } + // In case any setting changed remove the old settings file. + // This triggers a full rebuild on the next run if the current + // build is aborted before writing the current settings in the end. + if (this->SettingsChanged()) { + cmSystemTools::RemoveFile(this->SettingsFile); + } + } else { + // If the file could not be read re-generate everythiung. + this->MocSettingsChanged = true; + this->UicSettingsChanged = true; + } +} + +bool cmQtAutoGeneratorMocUic::SettingsFileWrite() +{ + bool success = true; + // Only write if any setting changed + if (this->SettingsChanged()) { + if (this->Verbose) { + this->LogInfo(cmQtAutoGen::GEN, "Writing settings file " + + cmQtAutoGen::Quoted(this->SettingsFile)); + } + // Compose settings file content + std::string settings; + { + auto SettingAppend = [&settings](const char* key, + std::string const& value) { + settings += "set("; + settings += key; + settings += " "; + settings += cmOutputConverter::EscapeForCMake(value); + settings += ")\n"; + }; + SettingAppend(SettingsKeyMoc, this->SettingsStringMoc); + SettingAppend(SettingsKeyUic, this->SettingsStringUic); + } + // Write settings file + if (!this->FileWrite(cmQtAutoGen::GEN, this->SettingsFile, settings)) { + this->LogFileError(cmQtAutoGen::GEN, this->SettingsFile, + "Settings file writing failed"); + // Remove old settings file to trigger a full rebuild on the next run + cmSystemTools::RemoveFile(this->SettingsFile); + success = false; + } + } + return success; +} + +bool cmQtAutoGeneratorMocUic::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 + // what file the moc is created from. Once a moc is included the same moc + // may not be included in the mocs_compilation.cpp file anymore. + // OTOH if there's a header containing Q_OBJECT where no corresponding + // moc file is included anywhere a moc_.cpp file is created and + // included in the mocs_compilation.cpp file. + + // Create AUTOGEN include directory + { + std::string const incDirAbs = cmSystemTools::CollapseCombinedPath( + this->AutogenBuildDir, this->AutogenIncludeDir); + if (!cmSystemTools::MakeDirectory(incDirAbs)) { + this->LogFileError(cmQtAutoGen::GEN, incDirAbs, + "Could not create directory"); + return false; + } + } + + // Parse source files + for (const auto& item : this->SourceJobs) { + if (!this->ParseSourceFile(item.first, item.second)) { + return false; + } + } + // Parse header files + for (const auto& item : this->HeaderJobs) { + if (!this->ParseHeaderFile(item.first, item.second)) { + return false; + } + } + // Read missing dependency information + if (!this->ParsePostprocess()) { + return false; + } + + // Generate files + if (!this->MocGenerateAll()) { + return false; + } + if (!this->UicGenerateAll()) { + return false; + } + + return true; +} + +/** + * @return True on success + */ +bool cmQtAutoGeneratorMocUic::ParseSourceFile(std::string const& absFilename, + const SourceJob& job) +{ + std::string contentText; + std::string error; + bool success = 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 success; +} + +/** + * @return True on success + */ +bool cmQtAutoGeneratorMocUic::ParseHeaderFile(std::string const& absFilename, + const SourceJob& job) +{ + 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; +} + +/** + * @return True on success + */ +bool cmQtAutoGeneratorMocUic::ParsePostprocess() +{ + bool success = true; + // Read missing dependencies + for (auto& item : this->MocJobsIncluded) { + if (!item->DependsValid) { + std::string content; + std::string error; + if (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 success; +} + +/** + * @brief Tests if the file should be ignored for moc scanning + * @return True if the file should be ignored + */ +bool cmQtAutoGeneratorMocUic::MocSkip(std::string const& absFilename) const +{ + if (this->MocEnabled()) { + // Test if the file name is on the skip list + if (!ListContains(this->MocSkipList, absFilename)) { + return false; + } + } + return true; +} + +/** + * @brief Tests if the C++ content requires moc processing + * @return True if moc is required + */ +bool cmQtAutoGeneratorMocUic::MocRequired(std::string const& contentText, + std::string* macroName) +{ + for (KeyRegExp& filter : this->MocMacroFilters) { + // Run a simple find string operation before the expensive + // regular expression check + if (contentText.find(filter.Key) != std::string::npos) { + if (filter.RegExp.find(contentText)) { + // Return macro name on demand + if (macroName != nullptr) { + *macroName = filter.Key; + } + return true; + } + } + } + return false; +} + +std::string cmQtAutoGeneratorMocUic::MocStringMacros() const +{ + std::string res; + const auto itB = this->MocMacroFilters.cbegin(); + const auto itE = this->MocMacroFilters.cend(); + const auto itL = itE - 1; + auto itC = itB; + for (; itC != itE; ++itC) { + // Separator + if (itC != itB) { + if (itC != itL) { + res += ", "; + } else { + res += " or "; + } + } + // Key + res += itC->Key; + } + return res; +} + +std::string cmQtAutoGeneratorMocUic::MocStringHeaders( + std::string const& fileBase) const +{ + std::string res = fileBase; + res += ".{"; + res += cmJoin(this->HeaderExtensions, ","); + res += "}"; + return res; +} + +std::string cmQtAutoGeneratorMocUic::MocFindIncludedHeader( + std::string const& sourcePath, std::string const& includeBase) const +{ + std::string header; + // Search in vicinity of the source + if (!this->FindHeader(header, sourcePath + includeBase)) { + // Search in include directories + for (std::string const& path : this->MocIncludePaths) { + std::string fullPath = path; + fullPath.push_back('/'); + fullPath += includeBase; + if (this->FindHeader(header, fullPath)) { + break; + } + } + } + // Sanitize + if (!header.empty()) { + header = cmSystemTools::GetRealPath(header); + } + return header; +} + +bool cmQtAutoGeneratorMocUic::MocFindIncludedFile( + std::string& absFile, std::string const& sourcePath, + std::string const& includeString) const +{ + bool success = false; + // Search in vicinity of the source + { + std::string testPath = sourcePath; + testPath += includeString; + if (cmSystemTools::FileExists(testPath.c_str())) { + absFile = cmSystemTools::GetRealPath(testPath); + success = true; + } + } + // Search in include directories + if (!success) { + for (std::string const& path : this->MocIncludePaths) { + std::string fullPath = path; + fullPath.push_back('/'); + fullPath += includeString; + if (cmSystemTools::FileExists(fullPath.c_str())) { + absFile = cmSystemTools::GetRealPath(fullPath); + success = true; + break; + } + } + } + return success; +} + +bool cmQtAutoGeneratorMocUic::MocDependFilterPush(std::string const& key, + std::string const& regExp) +{ + std::string error; + if (!key.empty()) { + if (!regExp.empty()) { + KeyRegExp filter; + filter.Key = key; + if (filter.RegExp.compile(regExp)) { + this->MocDependFilters.push_back(std::move(filter)); + } else { + error = "Regular expression compiling failed"; + } + } else { + error = "Regular expression is empty"; + } + } else { + error = "Key is empty"; + } + if (!error.empty()) { + std::string emsg = "AUTOMOC_DEPEND_FILTERS: "; + emsg += error; + emsg += "\n"; + emsg += " Key: "; + emsg += cmQtAutoGen::Quoted(key); + emsg += "\n"; + emsg += " RegExp: "; + emsg += cmQtAutoGen::Quoted(regExp); + emsg += "\n"; + this->LogError(cmQtAutoGen::MOC, emsg); + return false; + } + return true; +} + +void cmQtAutoGeneratorMocUic::MocFindDepends(std::string const& absFilename, + std::string const& contentText, + std::set& depends) +{ + if (this->MocDependFilters.empty() && contentText.empty()) { + return; + } + + std::vector 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 cmQtAutoGeneratorMocUic::MocParseSourceContent( + std::string const& absFilename, std::string const& contentText) +{ + if (this->Verbose) { + 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(); + job->SourceFile = sourceFile; + job->BuildFileRel = this->AutogenIncludeDir; + job->BuildFileRel += includeString; + job->Includer = absFilename; + job->IncludeString = includeString; + job->DependsValid = (content != nullptr); + if (job->DependsValid) { + this->MocFindDepends(sourceFile, *content, job->Depends); + } + this->MocJobsIncluded.push_back(std::move(job)); + }; + + struct MocInc + { + std::string Inc; // full include string + std::string Dir; // include string directory + std::string Base; // include string file base + }; + + // Extract moc includes from file + std::vector mocIncsUsc; + std::vector 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_.cxx + // Remove the moc_ part from the base name + mocIncsUsc.push_back(MocInc{ std::move(incString), std::move(incDir), + incBase.substr(4) }); + } else { + // .moc + mocIncsDot.push_back(MocInc{ std::move(incString), std::move(incDir), + std::move(incBase) }); + } + // Forward content pointer + contentChars += this->MocRegExpInclude.end(); + } + } + } + + std::string selfMacroName; + const bool selfRequiresMoc = this->MocRequired(contentText, &selfMacroName); + + // Check if there is anything to do + if (!selfRequiresMoc && mocIncsUsc.empty() && mocIncsDot.empty()) { + return true; + } + + // Scan file variables + std::string const scanFileDir = SubDirPrefix(absFilename); + std::string const scanFileBase = + cmSystemTools::GetFilenameWithoutLastExtension(absFilename); + // Relaxed mode variables + bool ownDotMocIncluded = false; + std::string ownMocUscInclude; + std::string ownMocUscHeader; + + // Process moc_.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 .moc includes + for (const MocInc& mocInc : mocIncsDot) { + const bool ownMoc = (mocInc.Base == scanFileBase); + if (this->MocRelaxedMode) { + // Relaxed mode + if (selfRequiresMoc && ownMoc) { + // Add self + AddJob(absFilename, mocInc.Inc, &contentText); + ownDotMocIncluded = true; + } else { + // In relaxed mode try to find a header instead but issue a warning. + // This is for KDE4 compatibility + std::string const header = + this->MocFindIncludedHeader(scanFileDir, mocInc.Dir + mocInc.Base); + if (!header.empty()) { + // Check if header is skipped + if (this->MocSkip(header)) { + continue; + } + // Register moc job + AddJob(header, mocInc.Inc, nullptr); + if (!selfRequiresMoc) { + if (ownMoc) { + std::string emsg = "The file includes the moc file "; + emsg += cmQtAutoGen::Quoted(mocInc.Inc); + emsg += ", but does not contain a "; + emsg += this->MocStringMacros(); + emsg += " macro.\nRunning moc on\n "; + emsg += cmQtAutoGen::Quoted(header); + emsg += "!\nBetter include "; + emsg += cmQtAutoGen::Quoted("moc_" + mocInc.Base + ".cpp"); + emsg += " for a compatibility with strict mode.\n" + "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n"; + this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg); + } else { + std::string emsg = "The file includes the moc file "; + emsg += cmQtAutoGen::Quoted(mocInc.Inc); + emsg += " instead of "; + emsg += cmQtAutoGen::Quoted("moc_" + mocInc.Base + ".cpp"); + emsg += ".\nRunning moc on\n "; + emsg += cmQtAutoGen::Quoted(header); + emsg += "!\nBetter include "; + emsg += cmQtAutoGen::Quoted("moc_" + mocInc.Base + ".cpp"); + emsg += " for compatibility with strict mode.\n" + "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n"; + this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg); + } + } + } else { + std::string emsg = "The file includes the moc file "; + emsg += cmQtAutoGen::Quoted(mocInc.Inc); + emsg += ", which seems to be the moc file from a different " + "source file. CMake also could not find a matching " + "header."; + this->LogFileError(cmQtAutoGen::MOC, absFilename, emsg); + return false; + } + } + } else { + // Strict mode + if (ownMoc) { + // Include self + AddJob(absFilename, mocInc.Inc, &contentText); + ownDotMocIncluded = true; + // Accept but issue a warning if moc isn't required + if (!selfRequiresMoc) { + std::string emsg = "The file includes the moc file "; + emsg += cmQtAutoGen::Quoted(mocInc.Inc); + emsg += ", but does not contain a "; + emsg += this->MocStringMacros(); + emsg += " macro."; + this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg); + } + } else { + // Don't allow .moc include other than self in strict mode + std::string emsg = "The file includes the moc file "; + emsg += cmQtAutoGen::Quoted(mocInc.Inc); + emsg += ", which seems to be the moc file from a different " + "source file.\nThis is not supported. Include "; + emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc"); + emsg += " to run moc on this source file."; + this->LogFileError(cmQtAutoGen::MOC, absFilename, emsg); + return false; + } + } + } + + if (selfRequiresMoc && !ownDotMocIncluded) { + // In this case, check whether the scanned file itself contains a Q_OBJECT. + // If this is the case, the moc_foo.cpp should probably be generated from + // foo.cpp instead of foo.h, because otherwise it won't build. + // But warn, since this is not how it is supposed to be used. + if (this->MocRelaxedMode && !ownMocUscInclude.empty()) { + // This is for KDE4 compatibility: + std::string emsg = "The file contains a "; + emsg += selfMacroName; + emsg += " macro, but does not include "; + emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc"); + emsg += ". Instead it includes "; + emsg += cmQtAutoGen::Quoted(ownMocUscInclude); + emsg += ".\nRunning moc on\n "; + emsg += cmQtAutoGen::Quoted(absFilename); + emsg += "!\nBetter include "; + emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc"); + emsg += " for compatibility with strict mode.\n" + "(CMAKE_AUTOMOC_RELAXED_MODE warning)"; + this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg); + + // Remove own header job + { + auto itC = this->MocJobsIncluded.begin(); + auto itE = this->MocJobsIncluded.end(); + for (; itC != itE; ++itC) { + if ((*itC)->SourceFile == ownMocUscHeader) { + if ((*itC)->IncludeString == ownMocUscInclude) { + this->MocJobsIncluded.erase(itC); + break; + } + } + } + } + // Add own source job + AddJob(absFilename, ownMocUscInclude, &contentText); + } else { + // Otherwise always error out since it will not compile: + std::string emsg = "The file contains a "; + emsg += selfMacroName; + emsg += " macro, but does not include "; + emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc"); + emsg += "!\nConsider to\n - add #include \""; + emsg += scanFileBase; + emsg += ".moc\"\n - enable SKIP_AUTOMOC for this file"; + this->LogFileError(cmQtAutoGen::MOC, absFilename, emsg); + return false; + } + } + return true; +} + +void cmQtAutoGeneratorMocUic::MocParseHeaderContent( + std::string const& absFilename, std::string const& contentText) +{ + if (this->Verbose) { + this->LogInfo(cmQtAutoGen::MOC, "Checking: " + absFilename); + } + + auto const fit = + std::find_if(this->MocJobsIncluded.cbegin(), this->MocJobsIncluded.cend(), + [&absFilename](std::unique_ptr const& job) { + return job->SourceFile == absFilename; + }); + if (fit == this->MocJobsIncluded.cend()) { + if (this->MocRequired(contentText)) { + auto job = cm::make_unique(); + job->SourceFile = absFilename; + { + std::string& bld = job->BuildFileRel; + bld = this->FilePathChecksum.getPart(absFilename); + bld += '/'; + bld += "moc_"; + bld += cmSystemTools::GetFilenameWithoutLastExtension(absFilename); + if (this->MultiConfig != cmQtAutoGen::SINGLE) { + bld += this->ConfigSuffix; + } + bld += ".cpp"; + } + this->MocFindDepends(absFilename, contentText, job->Depends); + this->MocJobsAuto.push_back(std::move(job)); + } + } +} + +bool cmQtAutoGeneratorMocUic::MocGenerateAll() +{ + if (!this->MocEnabled()) { + return true; + } + + // Look for name collisions in included moc files + { + bool collision = false; + std::map> 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_.cpp\" file\n" + " - add a directory prefix to a \".moc\" include " + "(e.g \"sub/.moc\")\n" + " - rename the source file(s)\n" + "Include conflicts\n" + "-----------------\n"; + const auto& colls = collisions; + for (auto const& coll : colls) { + if (coll.second.size() > 1) { + emsg += cmQtAutoGen::Quoted(coll.first); + emsg += " included in\n"; + for (const MocJobIncluded* job : coll.second) { + emsg += " - "; + emsg += cmQtAutoGen::Quoted(job->Includer); + emsg += "\n"; + } + emsg += "would be generated from\n"; + for (const MocJobIncluded* job : coll.second) { + emsg += " - "; + emsg += cmQtAutoGen::Quoted(job->SourceFile); + emsg += "\n"; + } + } + } + this->LogError(cmQtAutoGen::MOC, emsg); + return false; + } + } + + // (Re)generate moc_predefs.h on demand + if (!this->MocPredefsCmd.empty()) { + if (this->MocSettingsChanged || + !cmSystemTools::FileExists(this->MocPredefsFileAbs)) { + if (this->Verbose) { + this->LogBold("Generating MOC predefs " + this->MocPredefsFileRel); + } + + std::string output; + { + // Compose command + std::vector cmd = this->MocPredefsCmd; + // Add includes + cmd.insert(cmd.end(), this->MocIncludes.begin(), + this->MocIncludes.end()); + // Add definitions + for (std::string const& def : this->MocDefinitions) { + cmd.push_back("-D" + def); + } + // Execute command + if (!this->RunCommand(cmd, output)) { + this->LogCommandError(cmQtAutoGen::MOC, + "moc_predefs generation failed", cmd, output); + return false; + } + } + + // (Re)write predefs file only on demand + if (this->FileDiffers(this->MocPredefsFileAbs, output)) { + if (this->FileWrite(cmQtAutoGen::MOC, this->MocPredefsFileAbs, + output)) { + this->MocPredefsChanged = true; + } else { + this->LogFileError(cmQtAutoGen::MOC, this->MocPredefsFileAbs, + "moc_predefs file writing failed"); + return false; + } + } else { + // Touch to update the time stamp + if (this->Verbose) { + this->LogInfo(cmQtAutoGen::MOC, + "Touching moc_predefs " + this->MocPredefsFileRel); + } + cmSystemTools::Touch(this->MocPredefsFileAbs, false); + } + } + + // Add moc_predefs.h to moc file dependencies + for (auto const& item : this->MocJobsIncluded) { + item->Depends.insert(this->MocPredefsFileAbs); + } + for (auto const& item : this->MocJobsAuto) { + item->Depends.insert(this->MocPredefsFileAbs); + } + } + + // Generate moc files that are included by source files. + for (auto const& item : this->MocJobsIncluded) { + if (!this->MocGenerateFile(*item)) { + return false; + } + } + // Generate moc files that are _not_ included by source files. + bool autoNameGenerated = false; + for (auto const& item : this->MocJobsAuto) { + if (!this->MocGenerateFile(*item, &autoNameGenerated)) { + return false; + } + } + + // Compose mocs compilation file content + { + std::string mocs = + "// This file is autogenerated. Changes will be overwritten.\n"; + if (this->MocJobsAuto.empty()) { + // Placeholder content + mocs += + "// No files found that require moc or the moc files are included\n"; + mocs += "enum some_compilers { need_more_than_nothing };\n"; + } else { + // Valid content + for (const auto& item : this->MocJobsAuto) { + mocs += "#include \""; + mocs += item->BuildFileRel; + mocs += "\"\n"; + } + } + + if (this->FileDiffers(this->MocCompFileAbs, mocs)) { + // Actually write mocs compilation file + if (this->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); + } + } + + return true; +} + +/** + * @return True on success + */ +bool cmQtAutoGeneratorMocUic::MocGenerateFile(const MocJobAuto& mocJob, + bool* generated) +{ + bool success = true; + + std::string const mocFileAbs = cmSystemTools::CollapseCombinedPath( + this->AutogenBuildDir, mocJob.BuildFileRel); + + bool generate = false; + std::string generateReason; + if (!generate && !cmSystemTools::FileExists(mocFileAbs.c_str())) { + if (this->Verbose) { + generateReason = "Generating "; + generateReason += cmQtAutoGen::Quoted(mocFileAbs); + generateReason += " from its source file "; + generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile); + generateReason += " because it doesn't exist"; + } + generate = true; + } + if (!generate && this->MocSettingsChanged) { + if (this->Verbose) { + generateReason = "Generating "; + generateReason += cmQtAutoGen::Quoted(mocFileAbs); + generateReason += " from "; + generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile); + generateReason += " because the MOC settings changed"; + } + generate = true; + } + if (!generate && this->MocPredefsChanged) { + if (this->Verbose) { + generateReason = "Generating "; + generateReason += cmQtAutoGen::Quoted(mocFileAbs); + generateReason += " from "; + generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile); + generateReason += " because moc_predefs.h changed"; + } + generate = true; + } + if (!generate) { + std::string error; + if (FileIsOlderThan(mocFileAbs, mocJob.SourceFile, &error)) { + if (this->Verbose) { + generateReason = "Generating "; + generateReason += cmQtAutoGen::Quoted(mocFileAbs); + generateReason += " because it's older than its source file "; + generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile); + } + generate = true; + } else { + if (!error.empty()) { + this->LogError(cmQtAutoGen::MOC, error); + success = false; + } + } + } + if (success && !generate) { + // Test if a dependency file is newer + std::string error; + for (std::string const& depFile : mocJob.Depends) { + if (FileIsOlderThan(mocFileAbs, depFile, &error)) { + if (this->Verbose) { + generateReason = "Generating "; + generateReason += cmQtAutoGen::Quoted(mocFileAbs); + generateReason += " from "; + generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile); + generateReason += " because it is older than "; + generateReason += cmQtAutoGen::Quoted(depFile); + } + generate = true; + break; + } + if (!error.empty()) { + this->LogError(cmQtAutoGen::MOC, error); + success = false; + break; + } + } + } + + if (generate) { + // Log + if (this->Verbose) { + this->LogBold("Generating MOC source " + mocJob.BuildFileRel); + this->LogInfo(cmQtAutoGen::MOC, generateReason); + } + + // Make sure the parent directory exists + if (this->MakeParentDirectory(cmQtAutoGen::MOC, mocFileAbs)) { + // Compose moc command + std::vector cmd; + cmd.push_back(this->MocExecutable); + // Add options + cmd.insert(cmd.end(), this->MocAllOptions.begin(), + this->MocAllOptions.end()); + // Add predefs include + if (!this->MocPredefsFileAbs.empty()) { + cmd.push_back("--include"); + cmd.push_back(this->MocPredefsFileAbs); + } + cmd.push_back("-o"); + cmd.push_back(mocFileAbs); + cmd.push_back(mocJob.SourceFile); + + // Execute moc command + std::string output; + if (this->RunCommand(cmd, output)) { + // Success + if (generated != nullptr) { + *generated = true; + } + } else { + // Moc command failed + { + std::string emsg = "moc failed for\n "; + emsg += cmQtAutoGen::Quoted(mocJob.SourceFile); + this->LogCommandError(cmQtAutoGen::MOC, emsg, cmd, output); + } + cmSystemTools::RemoveFile(mocFileAbs); + success = false; + } + } else { + // Parent directory creation failed + success = false; + } + } + return success; +} + +/** + * @brief Tests if the file name is in the skip list + */ +bool cmQtAutoGeneratorMocUic::UicSkip(std::string const& absFilename) const +{ + if (this->UicEnabled()) { + // Test if the file name is on the skip list + if (!ListContains(this->UicSkipList, absFilename)) { + return false; + } + } + return true; +} + +bool cmQtAutoGeneratorMocUic::UicParseContent(std::string const& absFilename, + std::string const& contentText) +{ + if (this->Verbose) { + this->LogInfo(cmQtAutoGen::UIC, "Checking: " + absFilename); + } + + std::vector 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(); + job->SourceFile = uiInputFile; + job->BuildFileRel = this->AutogenIncludeDir; + job->BuildFileRel += includeString; + job->Includer = absFilename; + job->IncludeString = includeString; + this->UicJobs.push_back(std::move(job)); + } + } + + return true; +} + +bool cmQtAutoGeneratorMocUic::UicFindIncludedFile( + std::string& absFile, std::string const& sourceFile, + std::string const& includeString) +{ + bool success = false; + std::string searchFile = + cmSystemTools::GetFilenameWithoutLastExtension(includeString).substr(3); + searchFile += ".ui"; + // Collect search paths list + std::vector testFiles; + { + std::string const searchPath = SubDirPrefix(includeString); + + std::string searchFileFull; + if (!searchPath.empty()) { + searchFileFull = searchPath; + searchFileFull += searchFile; + } + // Vicinity of the source + { + std::string const sourcePath = SubDirPrefix(sourceFile); + testFiles.push_back(sourcePath + searchFile); + if (!searchPath.empty()) { + testFiles.push_back(sourcePath + searchFileFull); + } + } + // AUTOUIC search paths + if (!this->UicSearchPaths.empty()) { + for (std::string const& sPath : this->UicSearchPaths) { + testFiles.push_back((sPath + "/").append(searchFile)); + } + if (!searchPath.empty()) { + for (std::string const& sPath : this->UicSearchPaths) { + testFiles.push_back((sPath + "/").append(searchFileFull)); + } + } + } + } + + // Search for the .ui file! + for (std::string const& testFile : testFiles) { + if (cmSystemTools::FileExists(testFile.c_str())) { + absFile = cmSystemTools::GetRealPath(testFile); + success = true; + break; + } + } + + // Log error + if (!success) { + std::string emsg = "Could not find "; + emsg += cmQtAutoGen::Quoted(searchFile); + emsg += " in\n"; + for (std::string const& testFile : testFiles) { + emsg += " "; + emsg += cmQtAutoGen::Quoted(testFile); + emsg += "\n"; + } + this->LogFileError(cmQtAutoGen::UIC, sourceFile, emsg); + } + + return success; +} + +bool cmQtAutoGeneratorMocUic::UicGenerateAll() +{ + if (!this->UicEnabled()) { + return true; + } + + // Look for name collisions in included uic files + { + bool collision = false; + std::map> collisions; + for (auto const& job : this->UicJobs) { + auto& list = collisions[job->IncludeString]; + if (!list.empty()) { + collision = true; + } + list.push_back(job.get()); + } + if (collision) { + std::string emsg = + "Included uic files with the same name will be " + "generated from different sources.\n" + "Consider to\n" + " - add a directory prefix to a \"ui_.h\" include " + "(e.g \"sub/ui_.h\")\n" + " - rename the .ui file(s) and adjust the \"ui_.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 header files + for (const auto& item : this->UicJobs) { + if (!this->UicGenerateFile(*item)) { + return false; + } + } + + return true; +} + +/** + * @return True on success + */ +bool cmQtAutoGeneratorMocUic::UicGenerateFile(const UicJob& uicJob) +{ + bool success = true; + + std::string const uicFileAbs = cmSystemTools::CollapseCombinedPath( + this->AutogenBuildDir, uicJob.BuildFileRel); + + bool generate = false; + std::string generateReason; + if (!generate && !cmSystemTools::FileExists(uicFileAbs.c_str())) { + if (this->Verbose) { + generateReason = "Generating "; + generateReason += cmQtAutoGen::Quoted(uicFileAbs); + generateReason += " from its source file "; + generateReason += cmQtAutoGen::Quoted(uicJob.SourceFile); + generateReason += " because it doesn't exist"; + } + generate = true; + } + if (!generate && this->UicSettingsChanged) { + if (this->Verbose) { + generateReason = "Generating "; + generateReason += cmQtAutoGen::Quoted(uicFileAbs); + generateReason += " from "; + generateReason += cmQtAutoGen::Quoted(uicJob.SourceFile); + generateReason += " because the UIC settings changed"; + } + generate = true; + } + if (!generate) { + std::string error; + if (FileIsOlderThan(uicFileAbs, uicJob.SourceFile, &error)) { + if (this->Verbose) { + generateReason = "Generating "; + generateReason += cmQtAutoGen::Quoted(uicFileAbs); + generateReason += " because it's older than its source file "; + generateReason += cmQtAutoGen::Quoted(uicJob.SourceFile); + } + generate = true; + } else { + if (!error.empty()) { + this->LogError(cmQtAutoGen::UIC, error); + success = false; + } + } + } + if (generate) { + // Log + if (this->Verbose) { + this->LogBold("Generating UIC header " + uicJob.BuildFileRel); + this->LogInfo(cmQtAutoGen::UIC, generateReason); + } + + // Make sure the parent directory exists + if (this->MakeParentDirectory(cmQtAutoGen::UIC, uicFileAbs)) { + // Compose uic command + std::vector cmd; + cmd.push_back(this->UicExecutable); + { + std::vector allOpts = this->UicTargetOptions; + auto optionIt = this->UicOptions.find(uicJob.SourceFile); + if (optionIt != this->UicOptions.end()) { + cmQtAutoGen::UicMergeOptions(allOpts, optionIt->second, + (this->QtMajorVersion == "5")); + } + cmd.insert(cmd.end(), allOpts.begin(), allOpts.end()); + } + cmd.push_back("-o"); + cmd.push_back(uicFileAbs); + cmd.push_back(uicJob.SourceFile); + + std::string output; + if (this->RunCommand(cmd, output)) { + // Success + } else { + // Command failed + { + std::string emsg = "uic failed for\n "; + emsg += cmQtAutoGen::Quoted(uicJob.SourceFile); + emsg += "\nincluded by\n "; + emsg += cmQtAutoGen::Quoted(uicJob.Includer); + this->LogCommandError(cmQtAutoGen::UIC, emsg, cmd, output); + } + cmSystemTools::RemoveFile(uicFileAbs); + success = false; + } + } else { + // Parent directory creation failed + success = false; + } + } + return success; +} + +void cmQtAutoGeneratorMocUic::LogBold(std::string const& message) const +{ + cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundBlue | + cmsysTerminal_Color_ForegroundBold, + message.c_str(), true, this->ColorOutput); +} + +void cmQtAutoGeneratorMocUic::LogInfo(cmQtAutoGen::Generator genType, + std::string const& message) const +{ + std::string msg = cmQtAutoGen::GeneratorName(genType); + msg += ": "; + msg += message; + if (msg.back() != '\n') { + msg.push_back('\n'); + } + cmSystemTools::Stdout(msg.c_str(), msg.size()); +} + +void cmQtAutoGeneratorMocUic::LogWarning(cmQtAutoGen::Generator genType, + std::string const& message) const +{ + std::string msg = cmQtAutoGen::GeneratorName(genType); + msg += " warning:"; + if (message.find('\n') == std::string::npos) { + // Single line message + msg.push_back(' '); + } else { + // Multi line message + msg.push_back('\n'); + } + // Message + msg += message; + if (msg.back() != '\n') { + msg.push_back('\n'); + } + msg.push_back('\n'); + cmSystemTools::Stdout(msg.c_str(), msg.size()); +} + +void cmQtAutoGeneratorMocUic::LogFileWarning(cmQtAutoGen::Generator genType, + std::string const& filename, + std::string const& message) const +{ + std::string msg = " "; + msg += cmQtAutoGen::Quoted(filename); + msg.push_back('\n'); + // Message + msg += message; + this->LogWarning(genType, msg); +} + +void cmQtAutoGeneratorMocUic::LogError(cmQtAutoGen::Generator genType, + std::string const& message) const +{ + std::string msg; + msg.push_back('\n'); + msg += HeadLine(cmQtAutoGen::GeneratorName(genType) + " error"); + // Message + msg += message; + if (msg.back() != '\n') { + msg.push_back('\n'); + } + msg.push_back('\n'); + cmSystemTools::Stderr(msg.c_str(), msg.size()); +} + +void cmQtAutoGeneratorMocUic::LogFileError(cmQtAutoGen::Generator genType, + std::string const& filename, + std::string const& message) const +{ + std::string emsg = " "; + emsg += cmQtAutoGen::Quoted(filename); + emsg += '\n'; + // Message + emsg += message; + this->LogError(genType, emsg); +} + +void cmQtAutoGeneratorMocUic::LogCommandError( + cmQtAutoGen::Generator genType, std::string const& message, + std::vector const& command, std::string const& output) const +{ + std::string msg; + msg.push_back('\n'); + msg += HeadLine(cmQtAutoGen::GeneratorName(genType) + " subprocess error"); + msg += message; + if (msg.back() != '\n') { + msg.push_back('\n'); + } + msg.push_back('\n'); + msg += HeadLine("Command"); + msg += QuotedCommand(command); + if (msg.back() != '\n') { + msg.push_back('\n'); + } + msg.push_back('\n'); + msg += HeadLine("Output"); + msg += output; + if (msg.back() != '\n') { + msg.push_back('\n'); + } + msg.push_back('\n'); + cmSystemTools::Stderr(msg.c_str(), msg.size()); +} + +/** + * @brief Generates the parent directory of the given file on demand + * @return True on success + */ +bool cmQtAutoGeneratorMocUic::MakeParentDirectory( + cmQtAutoGen::Generator genType, std::string const& filename) const +{ + bool success = true; + std::string const dirName = cmSystemTools::GetFilenamePath(filename); + if (!dirName.empty()) { + if (!cmSystemTools::MakeDirectory(dirName)) { + this->LogFileError(genType, filename, + "Could not create parent directory"); + success = false; + } + } + return success; +} + +bool cmQtAutoGeneratorMocUic::FileDiffers(std::string const& filename, + std::string const& content) +{ + bool differs = true; + { + std::string oldContents; + if (ReadFile(oldContents, filename)) { + differs = (oldContents != content); + } + } + return differs; +} + +bool cmQtAutoGeneratorMocUic::FileWrite(cmQtAutoGen::Generator genType, + 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::out | std::ios::binary | std::ios::trunc)); + if (outfile) { + outfile << content; + // Check for write errors + if (!outfile.good()) { + error = "File writing failed"; + } + } else { + error = "Opening file for writing failed"; + } + } + if (!error.empty()) { + this->LogFileError(genType, filename, error); + return false; + } + return true; +} + +/** + * @brief Runs a command and returns true on success + * @return True on success + */ +bool cmQtAutoGeneratorMocUic::RunCommand( + std::vector const& command, std::string& output) const +{ + // Log command + if (this->Verbose) { + std::string qcmd = QuotedCommand(command); + qcmd.push_back('\n'); + cmSystemTools::Stdout(qcmd.c_str(), qcmd.size()); + } + // Execute command + int retVal = 0; + bool res = cmSystemTools::RunSingleCommand( + command, &output, &output, &retVal, nullptr, cmSystemTools::OUTPUT_NONE); + return (res && (retVal == 0)); +} + +/** + * @brief Tries to find the header file to the given file base path by + * appending different header extensions + * @return True on success + */ +bool cmQtAutoGeneratorMocUic::FindHeader(std::string& header, + std::string const& testBasePath) const +{ + for (std::string const& ext : this->HeaderExtensions) { + std::string testFilePath(testBasePath); + testFilePath.push_back('.'); + testFilePath += ext; + if (cmSystemTools::FileExists(testFilePath.c_str())) { + header = testFilePath; + return true; + } + } + return false; +} diff --git a/Source/cmQtAutoGeneratorMocUic.h b/Source/cmQtAutoGeneratorMocUic.h new file mode 100644 index 0000000..579b4f4 --- /dev/null +++ b/Source/cmQtAutoGeneratorMocUic.h @@ -0,0 +1,228 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef cmQtAutoGeneratorMocUic_h +#define cmQtAutoGeneratorMocUic_h + +#include "cmConfigure.h" // IWYU pragma: keep + +#include "cmFilePathChecksum.h" +#include "cmQtAutoGen.h" +#include "cmsys/RegularExpression.hxx" + +#include +#include // IWYU pragma: keep +#include +#include +#include + +class cmMakefile; + +class cmQtAutoGeneratorMocUic +{ + CM_DISABLE_COPY(cmQtAutoGeneratorMocUic) +public: + cmQtAutoGeneratorMocUic(); + bool Run(std::string const& targetDirectory, std::string const& config); + +private: + // -- Types + + /// @brief Search key plus regular expression pair + struct KeyRegExp + { + KeyRegExp() = default; + + KeyRegExp(const char* key, const char* regExp) + : Key(key) + , RegExp(regExp) + { + } + + KeyRegExp(std::string const& key, std::string const& regExp) + : Key(key) + , RegExp(regExp) + { + } + + std::string Key; + 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 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; + }; + + // -- Initialization + bool InitInfoFile(cmMakefile* makefile, std::string const& targetDirectory, + std::string const& config); + + // -- Settings file + void SettingsFileRead(cmMakefile* makefile); + bool SettingsFileWrite(); + bool SettingsChanged() const + { + return (this->MocSettingsChanged || this->UicSettingsChanged); + } + + // -- Central processing + bool Process(); + + // -- Source parsing + bool ParseSourceFile(std::string const& absFilename, const SourceJob& job); + bool ParseHeaderFile(std::string const& absFilename, const SourceJob& job); + bool ParsePostprocess(); + + // -- Moc + bool MocEnabled() const { return !this->MocExecutable.empty(); } + bool MocSkip(std::string const& absFilename) const; + bool MocRequired(std::string const& contentText, + std::string* macroName = nullptr); + // Moc strings + std::string MocStringMacros() const; + std::string MocStringHeaders(std::string const& fileBase) const; + std::string MocFindIncludedHeader(std::string const& sourcePath, + std::string const& includeBase) const; + bool MocFindIncludedFile(std::string& absFile, std::string const& sourceFile, + std::string const& includeString) const; + // Moc depends + bool MocDependFilterPush(std::string const& key, std::string const& regExp); + void MocFindDepends(std::string const& absFilename, + std::string const& contentText, + std::set& depends); + // Moc + bool MocParseSourceContent(std::string const& absFilename, + std::string const& contentText); + void MocParseHeaderContent(std::string const& absFilename, + std::string const& contentText); + + bool MocGenerateAll(); + bool MocGenerateFile(const MocJobAuto& mocJob, bool* generated = nullptr); + + // -- Uic + bool UicEnabled() const { return !this->UicExecutable.empty(); } + bool UicSkip(std::string const& absFilename) const; + bool UicParseContent(std::string const& fileName, + std::string const& contentText); + bool UicFindIncludedFile(std::string& absFile, std::string const& sourceFile, + std::string const& includeString); + bool UicGenerateAll(); + bool UicGenerateFile(const UicJob& uicJob); + + // -- Log info + void LogBold(std::string const& message) const; + void LogInfo(cmQtAutoGen::Generator genType, + std::string const& message) const; + // -- Log warning + void LogWarning(cmQtAutoGen::Generator genType, + std::string const& message) const; + void LogFileWarning(cmQtAutoGen::Generator genType, + std::string const& filename, + std::string const& message) const; + // -- Log error + void LogError(cmQtAutoGen::Generator genType, + std::string const& message) const; + void LogFileError(cmQtAutoGen::Generator genType, + std::string const& filename, + std::string const& message) const; + void LogCommandError(cmQtAutoGen::Generator genType, + std::string const& message, + std::vector const& command, + std::string const& output) const; + + // -- Utility + bool MakeParentDirectory(cmQtAutoGen::Generator genType, + std::string const& filename) const; + bool FileDiffers(std::string const& filename, std::string const& content); + bool FileWrite(cmQtAutoGen::Generator genType, std::string const& filename, + std::string const& content); + bool FindHeader(std::string& header, std::string const& testBasePath) const; + bool RunCommand(std::vector const& command, + std::string& output) const; + + // -- Meta + std::string InfoFile; + std::string ConfigSuffix; + cmQtAutoGen::MultiConfig MultiConfig; + // -- Settings + bool IncludeProjectDirsBefore; + bool Verbose; + bool ColorOutput; + std::string SettingsFile; + std::string SettingsStringMoc; + std::string SettingsStringUic; + // -- Directories + std::string ProjectSourceDir; + std::string ProjectBinaryDir; + std::string CurrentSourceDir; + std::string CurrentBinaryDir; + std::string AutogenBuildDir; + std::string AutogenIncludeDir; + // -- Qt environment + std::string QtMajorVersion; + std::string QtMinorVersion; + std::string MocExecutable; + std::string UicExecutable; + // -- File lists + std::map HeaderJobs; + std::map SourceJobs; + std::vector HeaderExtensions; + cmFilePathChecksum FilePathChecksum; + // -- Moc + bool MocSettingsChanged; + bool MocPredefsChanged; + bool MocRelaxedMode; + std::string MocCompFileRel; + std::string MocCompFileAbs; + std::string MocPredefsFileRel; + std::string MocPredefsFileAbs; + std::vector MocSkipList; + std::vector MocIncludePaths; + std::vector MocIncludes; + std::vector MocDefinitions; + std::vector MocOptions; + std::vector MocAllOptions; + std::vector MocPredefsCmd; + std::vector MocDependFilters; + std::vector MocMacroFilters; + cmsys::RegularExpression MocRegExpInclude; + std::vector> MocJobsIncluded; + std::vector> MocJobsAuto; + // -- Uic + bool UicSettingsChanged; + std::vector UicSkipList; + std::vector UicTargetOptions; + std::map> UicOptions; + std::vector UicSearchPaths; + cmsys::RegularExpression UicRegExpInclude; + std::vector> UicJobs; +}; + +#endif diff --git a/Source/cmQtAutoGenerators.cxx b/Source/cmQtAutoGenerators.cxx deleted file mode 100644 index bc6a28e..0000000 --- a/Source/cmQtAutoGenerators.cxx +++ /dev/null @@ -1,2045 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#include "cmQtAutoGen.h" -#include "cmQtAutoGenerators.h" - -#include "cmsys/FStream.hxx" -#include "cmsys/Terminal.h" -#include -#include -#include -#include -#include -#include -#include - -#include "cmAlgorithms.h" -#include "cmCryptoHash.h" -#include "cmFilePathChecksum.h" -#include "cmGlobalGenerator.h" -#include "cmMakefile.h" -#include "cmOutputConverter.h" -#include "cmStateDirectory.h" -#include "cmStateSnapshot.h" -#include "cmSystemTools.h" -#include "cmake.h" - -#if defined(__APPLE__) -#include -#endif - -// -- Static variables - -static const char* SettingsKeyMoc = "AM_MOC_SETTINGS_HASH"; -static const char* SettingsKeyUic = "AM_UIC_SETTINGS_HASH"; - -// -- Static functions - -static std::string HeadLine(std::string const& title) -{ - std::string head = title; - head += '\n'; - head.append(head.size() - 1, '-'); - head += '\n'; - return head; -} - -static std::string QuotedCommand(std::vector const& command) -{ - std::string res; - for (std::string const& item : command) { - if (!res.empty()) { - res.push_back(' '); - } - std::string const cesc = cmQtAutoGen::Quoted(item); - if (item.empty() || (cesc.size() > (item.size() + 2)) || - (cesc.find(' ') != std::string::npos)) { - res += cesc; - } else { - res += item; - } - } - return res; -} - -static std::string SubDirPrefix(std::string const& fileName) -{ - std::string res(cmSystemTools::GetFilenamePath(fileName)); - if (!res.empty()) { - res += '/'; - } - return res; -} - -static bool ReadFile(std::string& content, std::string const& filename, - std::string* error = nullptr) -{ - bool success = false; - if (cmSystemTools::FileExists(filename)) { - std::size_t const length = cmSystemTools::FileLength(filename); - cmsys::ifstream ifs(filename.c_str(), (std::ios::in | std::ios::binary)); - if (ifs) { - content.resize(length); - ifs.read(&content.front(), content.size()); - 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; -} - -/** - * @brief Tests if buildFile is older than sourceFile - * @return True if buildFile is older than sourceFile. - * False may indicate an error. - */ -static bool FileIsOlderThan(std::string const& buildFile, - std::string const& sourceFile, - std::string* error = nullptr) -{ - int result = 0; - if (cmSystemTools::FileTimeCompare(buildFile, sourceFile, &result)) { - return (result < 0); - } - if (error != nullptr) { - error->append( - "File modification time comparison failed for the files\n "); - error->append(cmQtAutoGen::Quoted(buildFile)); - error->append("\nand\n "); - error->append(cmQtAutoGen::Quoted(sourceFile)); - } - return false; -} - -static bool ListContains(std::vector const& list, - std::string const& entry) -{ - return (std::find(list.begin(), list.end(), entry) != list.end()); -} - -// -- Class methods - -cmQtAutoGenerators::cmQtAutoGenerators() - : MultiConfig(cmQtAutoGen::WRAP) - , IncludeProjectDirsBefore(false) - , Verbose(cmSystemTools::HasEnv("VERBOSE")) - , ColorOutput(true) - , MocSettingsChanged(false) - , MocPredefsChanged(false) - , MocRelaxedMode(false) - , UicSettingsChanged(false) -{ - { - std::string colorEnv; - cmSystemTools::GetEnv("COLOR", colorEnv); - if (!colorEnv.empty()) { - this->ColorOutput = cmSystemTools::IsOn(colorEnv.c_str()); - } - } - - // Precompile regular expressions - this->MocRegExpInclude.compile( - "[\n][ \t]*#[ \t]*include[ \t]+" - "[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]"); - this->UicRegExpInclude.compile("[\n][ \t]*#[ \t]*include[ \t]+" - "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]"); -} - -bool cmQtAutoGenerators::Run(std::string const& targetDirectory, - std::string const& config) -{ - cmake cm(cmake::RoleScript); - cm.SetHomeOutputDirectory(targetDirectory); - cm.SetHomeDirectory(targetDirectory); - cm.GetCurrentSnapshot().SetDefaultDefinitions(); - cmGlobalGenerator gg(&cm); - - cmStateSnapshot snapshot = cm.GetCurrentSnapshot(); - snapshot.GetDirectory().SetCurrentBinary(targetDirectory); - snapshot.GetDirectory().SetCurrentSource(targetDirectory); - - auto makefile = cm::make_unique(&gg, snapshot); - gg.SetCurrentMakefile(makefile.get()); - - bool success = false; - if (this->InitInfoFile(makefile.get(), targetDirectory, config)) { - // Read latest settings - this->SettingsFileRead(makefile.get()); - if (this->Process()) { - // Write current settings - if (this->SettingsFileWrite()) { - success = true; - } - } - } - return success; -} - -bool cmQtAutoGenerators::InitInfoFile(cmMakefile* makefile, - std::string const& targetDirectory, - std::string const& config) -{ - // -- Meta - this->HeaderExtensions = makefile->GetCMakeInstance()->GetHeaderExtensions(); - - // Utility lambdas - auto InfoGet = [makefile](const char* key) { - return makefile->GetSafeDefinition(key); - }; - auto InfoGetBool = [makefile](const char* key) { - return makefile->IsOn(key); - }; - auto InfoGetList = [makefile](const char* key) -> std::vector { - std::vector list; - cmSystemTools::ExpandListArgument(makefile->GetSafeDefinition(key), list); - return list; - }; - auto InfoGetLists = - [makefile](const char* key) -> std::vector> { - std::vector> lists; - { - std::string const value = makefile->GetSafeDefinition(key); - std::string::size_type pos = 0; - while (pos < value.size()) { - std::string::size_type next = value.find(cmQtAutoGen::listSep, pos); - std::string::size_type length = - (next != std::string::npos) ? next - pos : value.size() - pos; - // Remove enclosing braces - if (length >= 2) { - std::string::const_iterator itBeg = value.begin() + (pos + 1); - std::string::const_iterator itEnd = itBeg + (length - 2); - { - std::string subValue(itBeg, itEnd); - std::vector list; - cmSystemTools::ExpandListArgument(subValue, list); - lists.push_back(std::move(list)); - } - } - pos += length; - pos += cmQtAutoGen::listSep.size(); - } - } - return lists; - }; - auto InfoGetConfig = [makefile, &config](const char* key) -> std::string { - const char* valueConf = nullptr; - { - std::string keyConf = key; - keyConf += '_'; - keyConf += config; - valueConf = makefile->GetDefinition(keyConf); - } - if (valueConf == nullptr) { - valueConf = makefile->GetSafeDefinition(key); - } - return std::string(valueConf); - }; - auto InfoGetConfigList = - [&InfoGetConfig](const char* key) -> std::vector { - std::vector list; - cmSystemTools::ExpandListArgument(InfoGetConfig(key), list); - return list; - }; - - // -- Read info file - this->InfoFile = cmSystemTools::CollapseFullPath(targetDirectory); - cmSystemTools::ConvertToUnixSlashes(this->InfoFile); - this->InfoFile += "/AutogenInfo.cmake"; - if (!makefile->ReadListFile(this->InfoFile.c_str())) { - this->LogFileError(cmQtAutoGen::GEN, this->InfoFile, - "File processing failed"); - return false; - } - - // -- Meta - this->MultiConfig = cmQtAutoGen::MultiConfigType(InfoGet("AM_MULTI_CONFIG")); - this->ConfigSuffix = InfoGetConfig("AM_CONFIG_SUFFIX"); - if (this->ConfigSuffix.empty()) { - this->ConfigSuffix = "_"; - this->ConfigSuffix += config; - } - - // - Files and directories - this->ProjectSourceDir = InfoGet("AM_CMAKE_SOURCE_DIR"); - this->ProjectBinaryDir = InfoGet("AM_CMAKE_BINARY_DIR"); - this->CurrentSourceDir = InfoGet("AM_CMAKE_CURRENT_SOURCE_DIR"); - this->CurrentBinaryDir = InfoGet("AM_CMAKE_CURRENT_BINARY_DIR"); - this->IncludeProjectDirsBefore = - InfoGetBool("AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE"); - this->AutogenBuildDir = InfoGet("AM_BUILD_DIR"); - if (this->AutogenBuildDir.empty()) { - this->LogFileError(cmQtAutoGen::GEN, this->InfoFile, - "Autogen build directory missing"); - return false; - } - - // - Qt environment - this->QtMajorVersion = InfoGet("AM_QT_VERSION_MAJOR"); - this->QtMinorVersion = InfoGet("AM_QT_VERSION_MINOR"); - this->MocExecutable = InfoGet("AM_QT_MOC_EXECUTABLE"); - this->UicExecutable = InfoGet("AM_QT_UIC_EXECUTABLE"); - - // Check Qt version - if ((this->QtMajorVersion != "4") && (this->QtMajorVersion != "5")) { - this->LogFileError(cmQtAutoGen::GEN, this->InfoFile, - "Unsupported Qt version: " + - cmQtAutoGen::Quoted(this->QtMajorVersion)); - return false; - } - - // - Moc - if (this->MocEnabled()) { - this->MocSkipList = InfoGetList("AM_MOC_SKIP"); - this->MocDefinitions = InfoGetConfigList("AM_MOC_DEFINITIONS"); -#ifdef _WIN32 - { - std::string const win32("WIN32"); - if (!ListContains(this->MocDefinitions, win32)) { - this->MocDefinitions.push_back(win32); - } - } -#endif - this->MocIncludePaths = InfoGetConfigList("AM_MOC_INCLUDES"); - this->MocOptions = InfoGetList("AM_MOC_OPTIONS"); - this->MocRelaxedMode = InfoGetBool("AM_MOC_RELAXED_MODE"); - { - std::vector const MocMacroNames = - InfoGetList("AM_MOC_MACRO_NAMES"); - for (std::string const& item : MocMacroNames) { - this->MocMacroFilters.emplace_back( - item, ("[\n][ \t]*{?[ \t]*" + item).append("[^a-zA-Z0-9_]")); - } - } - { - std::vector const mocDependFilters = - InfoGetList("AM_MOC_DEPEND_FILTERS"); - // Insert Q_PLUGIN_METADATA dependency filter - if (this->QtMajorVersion != "4") { - this->MocDependFilterPush("Q_PLUGIN_METADATA", - "[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\(" - "[^\\)]*FILE[ \t]*\"([^\"]+)\""); - } - // Insert user defined dependency filters - if ((mocDependFilters.size() % 2) == 0) { - for (std::vector::const_iterator - dit = mocDependFilters.begin(), - ditEnd = mocDependFilters.end(); - dit != ditEnd; dit += 2) { - if (!this->MocDependFilterPush(*dit, *(dit + 1))) { - return false; - } - } - } else { - this->LogFileError( - cmQtAutoGen::MOC, this->InfoFile, - "AUTOMOC_DEPEND_FILTERS list size is not a multiple of 2"); - return false; - } - } - this->MocPredefsCmd = InfoGetList("AM_MOC_PREDEFS_CMD"); - } - - // - Uic - if (this->UicEnabled()) { - this->UicSkipList = InfoGetList("AM_UIC_SKIP"); - this->UicSearchPaths = InfoGetList("AM_UIC_SEARCH_PATHS"); - this->UicTargetOptions = InfoGetConfigList("AM_UIC_TARGET_OPTIONS"); - { - auto sources = InfoGetList("AM_UIC_OPTIONS_FILES"); - auto options = InfoGetLists("AM_UIC_OPTIONS_OPTIONS"); - // Compare list sizes - if (sources.size() != options.size()) { - std::ostringstream ost; - ost << "files/options lists sizes missmatch (" << sources.size() << "/" - << options.size() << ")"; - 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; - } - } - } - - // Initialize source file jobs - { - // Utility lambdas - auto AddJob = [this](std::map& 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 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 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"; - if (this->MultiConfig != cmQtAutoGen::SINGLE) { - this->AutogenIncludeDir += this->ConfigSuffix; - } - this->AutogenIncludeDir += "/"; - - // Moc variables - if (this->MocEnabled()) { - // Mocs compilation file - this->MocCompFileRel = "mocs_compilation"; - if (this->MultiConfig == cmQtAutoGen::FULL) { - this->MocCompFileRel += this->ConfigSuffix; - } - this->MocCompFileRel += ".cpp"; - this->MocCompFileAbs = cmSystemTools::CollapseCombinedPath( - this->AutogenBuildDir, this->MocCompFileRel); - - // Moc predefs file - if (!this->MocPredefsCmd.empty()) { - this->MocPredefsFileRel = "moc_predefs"; - if (this->MultiConfig != cmQtAutoGen::SINGLE) { - 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 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 const movePaths = { - { &this->ProjectBinaryDir, &this->ProjectSourceDir } - }; - for (std::string const* ppath : movePaths) { - std::list::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 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 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()); - } - } - - // - Old settings file - { - this->SettingsFile = cmSystemTools::CollapseFullPath(targetDirectory); - cmSystemTools::ConvertToUnixSlashes(this->SettingsFile); - this->SettingsFile += "/AutogenOldSettings"; - if (this->MultiConfig != cmQtAutoGen::SINGLE) { - this->SettingsFile += this->ConfigSuffix; - } - this->SettingsFile += ".cmake"; - } - - return true; -} - -void cmQtAutoGenerators::SettingsFileRead(cmMakefile* makefile) -{ - // Compose current settings strings - { - cmCryptoHash crypt(cmCryptoHash::AlgoSHA256); - std::string const sep(" ~~~ "); - if (this->MocEnabled()) { - std::string str; - str += this->MocExecutable; - str += sep; - str += cmJoin(this->MocAllOptions, ";"); - str += sep; - str += this->IncludeProjectDirsBefore ? "TRUE" : "FALSE"; - str += sep; - str += cmJoin(this->MocPredefsCmd, ";"); - str += sep; - this->SettingsStringMoc = crypt.HashString(str); - } - if (this->UicEnabled()) { - std::string str; - str += this->UicExecutable; - str += sep; - str += cmJoin(this->UicTargetOptions, ";"); - for (const auto& item : this->UicOptions) { - str += sep; - str += item.first; - str += sep; - str += cmJoin(item.second, ";"); - } - str += sep; - this->SettingsStringUic = crypt.HashString(str); - } - } - - // Read old settings - if (makefile->ReadListFile(this->SettingsFile.c_str())) { - { - auto SMatch = [makefile](const char* key, std::string const& value) { - return (value == makefile->GetSafeDefinition(key)); - }; - if (!SMatch(SettingsKeyMoc, this->SettingsStringMoc)) { - this->MocSettingsChanged = true; - } - if (!SMatch(SettingsKeyUic, this->SettingsStringUic)) { - this->UicSettingsChanged = true; - } - } - // In case any setting changed remove the old settings file. - // This triggers a full rebuild on the next run if the current - // build is aborted before writing the current settings in the end. - if (this->SettingsChanged()) { - cmSystemTools::RemoveFile(this->SettingsFile); - } - } else { - // If the file could not be read re-generate everythiung. - this->MocSettingsChanged = true; - this->UicSettingsChanged = true; - } -} - -bool cmQtAutoGenerators::SettingsFileWrite() -{ - bool success = true; - // Only write if any setting changed - if (this->SettingsChanged()) { - if (this->Verbose) { - this->LogInfo(cmQtAutoGen::GEN, "Writing settings file " + - cmQtAutoGen::Quoted(this->SettingsFile)); - } - // Compose settings file content - std::string settings; - { - auto SettingAppend = [&settings](const char* key, - std::string const& value) { - settings += "set("; - settings += key; - settings += " "; - settings += cmOutputConverter::EscapeForCMake(value); - settings += ")\n"; - }; - SettingAppend(SettingsKeyMoc, this->SettingsStringMoc); - SettingAppend(SettingsKeyUic, this->SettingsStringUic); - } - // Write settings file - if (!this->FileWrite(cmQtAutoGen::GEN, this->SettingsFile, settings)) { - this->LogFileError(cmQtAutoGen::GEN, this->SettingsFile, - "Settings file writing failed"); - // Remove old settings file to trigger a full rebuild on the next run - cmSystemTools::RemoveFile(this->SettingsFile); - success = false; - } - } - return success; -} - -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 - // what file the moc is created from. Once a moc is included the same moc - // may not be included in the mocs_compilation.cpp file anymore. - // OTOH if there's a header containing Q_OBJECT where no corresponding - // moc file is included anywhere a moc_.cpp file is created and - // included in the mocs_compilation.cpp file. - - // Create AUTOGEN include directory - { - std::string const incDirAbs = cmSystemTools::CollapseCombinedPath( - this->AutogenBuildDir, this->AutogenIncludeDir); - if (!cmSystemTools::MakeDirectory(incDirAbs)) { - this->LogFileError(cmQtAutoGen::GEN, incDirAbs, - "Could not create directory"); - return false; - } - } - - // Parse source files - for (const auto& item : this->SourceJobs) { - if (!this->ParseSourceFile(item.first, item.second)) { - return false; - } - } - // Parse header files - for (const auto& item : this->HeaderJobs) { - if (!this->ParseHeaderFile(item.first, item.second)) { - return false; - } - } - // Read missing dependency information - if (!this->ParsePostprocess()) { - return false; - } - - // Generate files - if (!this->MocGenerateAll()) { - return false; - } - if (!this->UicGenerateAll()) { - return false; - } - - return true; -} - -/** - * @return True on success - */ -bool cmQtAutoGenerators::ParseSourceFile(std::string const& absFilename, - const SourceJob& job) -{ - 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 success; -} - -/** - * @return True on success - */ -bool cmQtAutoGenerators::ParseHeaderFile(std::string const& absFilename, - const SourceJob& job) -{ - 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; -} - -/** - * @return True on success - */ -bool cmQtAutoGenerators::ParsePostprocess() -{ - bool success = true; - // Read missing dependencies - 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 success; -} - -/** - * @brief Tests if the file should be ignored for moc scanning - * @return True if the file should be ignored - */ -bool cmQtAutoGenerators::MocSkip(std::string const& absFilename) const -{ - if (this->MocEnabled()) { - // Test if the file name is on the skip list - if (!ListContains(this->MocSkipList, absFilename)) { - return false; - } - } - return true; -} - -/** - * @brief Tests if the C++ content requires moc processing - * @return True if moc is required - */ -bool cmQtAutoGenerators::MocRequired(std::string const& contentText, - std::string* macroName) -{ - for (KeyRegExp& filter : this->MocMacroFilters) { - // Run a simple find string operation before the expensive - // regular expression check - if (contentText.find(filter.Key) != std::string::npos) { - if (filter.RegExp.find(contentText)) { - // Return macro name on demand - if (macroName != nullptr) { - *macroName = filter.Key; - } - return true; - } - } - } - return false; -} - -std::string cmQtAutoGenerators::MocStringMacros() const -{ - std::string res; - const auto itB = this->MocMacroFilters.cbegin(); - const auto itE = this->MocMacroFilters.cend(); - const auto itL = itE - 1; - auto itC = itB; - for (; itC != itE; ++itC) { - // Separator - if (itC != itB) { - if (itC != itL) { - res += ", "; - } else { - res += " or "; - } - } - // Key - res += itC->Key; - } - return res; -} - -std::string cmQtAutoGenerators::MocStringHeaders( - std::string const& fileBase) const -{ - 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& depends) -{ - if (this->MocDependFilters.empty() && contentText.empty()) { - return; - } - - std::vector 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(std::string const& absFilename, - std::string const& contentText) -{ - if (this->Verbose) { - 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(); - job->SourceFile = sourceFile; - job->BuildFileRel = this->AutogenIncludeDir; - job->BuildFileRel += includeString; - job->Includer = absFilename; - job->IncludeString = includeString; - job->DependsValid = (content != nullptr); - if (job->DependsValid) { - this->MocFindDepends(sourceFile, *content, job->Depends); - } - this->MocJobsIncluded.push_back(std::move(job)); - }; - - struct MocInc - { - std::string Inc; // full include string - std::string Dir; // include string directory - std::string Base; // include string file base - }; - - // Extract moc includes from file - std::vector mocIncsUsc; - std::vector 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_.cxx - // Remove the moc_ part from the base name - mocIncsUsc.push_back(MocInc{ std::move(incString), std::move(incDir), - incBase.substr(4) }); - } else { - // .moc - mocIncsDot.push_back(MocInc{ std::move(incString), std::move(incDir), - std::move(incBase) }); - } - // Forward content pointer - contentChars += this->MocRegExpInclude.end(); - } - } - } - - std::string selfMacroName; - const bool selfRequiresMoc = this->MocRequired(contentText, &selfMacroName); - - // Check if there is anything to do - if (!selfRequiresMoc && mocIncsUsc.empty() && mocIncsDot.empty()) { - return true; - } - - // Scan file variables - std::string const scanFileDir = SubDirPrefix(absFilename); - std::string const scanFileBase = - cmSystemTools::GetFilenameWithoutLastExtension(absFilename); - // Relaxed mode variables - bool ownDotMocIncluded = false; - std::string ownMocUscInclude; - std::string ownMocUscHeader; - - // Process moc_.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 .moc includes - for (const MocInc& mocInc : mocIncsDot) { - const bool ownMoc = (mocInc.Base == scanFileBase); - if (this->MocRelaxedMode) { - // Relaxed mode - if (selfRequiresMoc && ownMoc) { - // Add self - AddJob(absFilename, mocInc.Inc, &contentText); - ownDotMocIncluded = true; - } else { - // In relaxed mode try to find a header instead but issue a warning. - // This is for KDE4 compatibility - std::string const header = - this->MocFindIncludedHeader(scanFileDir, mocInc.Dir + mocInc.Base); - if (!header.empty()) { - // Check if header is skipped - if (this->MocSkip(header)) { - continue; - } - // Register moc job - AddJob(header, mocInc.Inc, nullptr); - if (!selfRequiresMoc) { - if (ownMoc) { - std::string emsg = "The file includes the moc file "; - emsg += cmQtAutoGen::Quoted(mocInc.Inc); - emsg += ", but does not contain a "; - emsg += this->MocStringMacros(); - emsg += " macro.\nRunning moc on\n "; - emsg += cmQtAutoGen::Quoted(header); - emsg += "!\nBetter include "; - emsg += cmQtAutoGen::Quoted("moc_" + mocInc.Base + ".cpp"); - emsg += " for a compatibility with strict mode.\n" - "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n"; - this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg); - } else { - std::string emsg = "The file includes the moc file "; - emsg += cmQtAutoGen::Quoted(mocInc.Inc); - emsg += " instead of "; - emsg += cmQtAutoGen::Quoted("moc_" + mocInc.Base + ".cpp"); - emsg += ".\nRunning moc on\n "; - emsg += cmQtAutoGen::Quoted(header); - emsg += "!\nBetter include "; - emsg += cmQtAutoGen::Quoted("moc_" + mocInc.Base + ".cpp"); - emsg += " for compatibility with strict mode.\n" - "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n"; - this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg); - } - } - } else { - std::string emsg = "The file includes the moc file "; - emsg += cmQtAutoGen::Quoted(mocInc.Inc); - emsg += ", which seems to be the moc file from a different " - "source file. CMake also could not find a matching " - "header."; - this->LogFileError(cmQtAutoGen::MOC, absFilename, emsg); - return false; - } - } - } else { - // Strict mode - if (ownMoc) { - // Include self - AddJob(absFilename, mocInc.Inc, &contentText); - ownDotMocIncluded = true; - // Accept but issue a warning if moc isn't required - if (!selfRequiresMoc) { - std::string emsg = "The file includes the moc file "; - emsg += cmQtAutoGen::Quoted(mocInc.Inc); - emsg += ", but does not contain a "; - emsg += this->MocStringMacros(); - emsg += " macro."; - this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg); - } - } else { - // Don't allow .moc include other than self in strict mode - std::string emsg = "The file includes the moc file "; - emsg += cmQtAutoGen::Quoted(mocInc.Inc); - emsg += ", which seems to be the moc file from a different " - "source file.\nThis is not supported. Include "; - emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc"); - emsg += " to run moc on this source file."; - this->LogFileError(cmQtAutoGen::MOC, absFilename, emsg); - return false; - } - } - } - - if (selfRequiresMoc && !ownDotMocIncluded) { - // In this case, check whether the scanned file itself contains a Q_OBJECT. - // If this is the case, the moc_foo.cpp should probably be generated from - // foo.cpp instead of foo.h, because otherwise it won't build. - // But warn, since this is not how it is supposed to be used. - if (this->MocRelaxedMode && !ownMocUscInclude.empty()) { - // This is for KDE4 compatibility: - std::string emsg = "The file contains a "; - emsg += selfMacroName; - emsg += " macro, but does not include "; - emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc"); - emsg += ". Instead it includes "; - emsg += cmQtAutoGen::Quoted(ownMocUscInclude); - emsg += ".\nRunning moc on\n "; - emsg += cmQtAutoGen::Quoted(absFilename); - emsg += "!\nBetter include "; - emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc"); - emsg += " for compatibility with strict mode.\n" - "(CMAKE_AUTOMOC_RELAXED_MODE warning)"; - this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg); - - // Remove own header job - { - auto itC = this->MocJobsIncluded.begin(); - auto itE = this->MocJobsIncluded.end(); - for (; itC != itE; ++itC) { - if ((*itC)->SourceFile == ownMocUscHeader) { - if ((*itC)->IncludeString == ownMocUscInclude) { - this->MocJobsIncluded.erase(itC); - break; - } - } - } - } - // Add own source job - AddJob(absFilename, ownMocUscInclude, &contentText); - } else { - // Otherwise always error out since it will not compile: - std::string emsg = "The file contains a "; - emsg += selfMacroName; - emsg += " macro, but does not include "; - emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc"); - emsg += "!\nConsider to\n - add #include \""; - emsg += scanFileBase; - emsg += ".moc\"\n - enable SKIP_AUTOMOC for this file"; - this->LogFileError(cmQtAutoGen::MOC, absFilename, emsg); - return false; - } - } - return true; -} - -void cmQtAutoGenerators::MocParseHeaderContent(std::string const& absFilename, - std::string const& contentText) -{ - if (this->Verbose) { - this->LogInfo(cmQtAutoGen::MOC, "Checking: " + absFilename); - } - - auto const fit = - std::find_if(this->MocJobsIncluded.cbegin(), this->MocJobsIncluded.cend(), - [&absFilename](std::unique_ptr const& job) { - return job->SourceFile == absFilename; - }); - if (fit == this->MocJobsIncluded.cend()) { - if (this->MocRequired(contentText)) { - auto job = cm::make_unique(); - job->SourceFile = absFilename; - { - std::string& bld = job->BuildFileRel; - bld = this->FilePathChecksum.getPart(absFilename); - bld += '/'; - bld += "moc_"; - bld += cmSystemTools::GetFilenameWithoutLastExtension(absFilename); - if (this->MultiConfig != cmQtAutoGen::SINGLE) { - bld += this->ConfigSuffix; - } - bld += ".cpp"; - } - this->MocFindDepends(absFilename, contentText, job->Depends); - this->MocJobsAuto.push_back(std::move(job)); - } - } -} - -bool cmQtAutoGenerators::MocGenerateAll() -{ - if (!this->MocEnabled()) { - return true; - } - - // Look for name collisions in included moc files - { - bool collision = false; - std::map> 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_.cpp\" file\n" - " - add a directory prefix to a \".moc\" include " - "(e.g \"sub/.moc\")\n" - " - rename the source file(s)\n" - "Include conflicts\n" - "-----------------\n"; - const auto& colls = collisions; - for (auto const& coll : colls) { - if (coll.second.size() > 1) { - emsg += cmQtAutoGen::Quoted(coll.first); - emsg += " included in\n"; - for (const MocJobIncluded* job : coll.second) { - emsg += " - "; - emsg += cmQtAutoGen::Quoted(job->Includer); - emsg += "\n"; - } - emsg += "would be generated from\n"; - for (const MocJobIncluded* job : coll.second) { - emsg += " - "; - emsg += cmQtAutoGen::Quoted(job->SourceFile); - emsg += "\n"; - } - } - } - this->LogError(cmQtAutoGen::MOC, emsg); - return false; - } - } - - // (Re)generate moc_predefs.h on demand - if (!this->MocPredefsCmd.empty()) { - if (this->MocSettingsChanged || - !cmSystemTools::FileExists(this->MocPredefsFileAbs)) { - if (this->Verbose) { - this->LogBold("Generating MOC predefs " + this->MocPredefsFileRel); - } - - std::string output; - { - // Compose command - std::vector cmd = this->MocPredefsCmd; - // Add includes - cmd.insert(cmd.end(), this->MocIncludes.begin(), - this->MocIncludes.end()); - // Add definitions - for (std::string const& def : this->MocDefinitions) { - cmd.push_back("-D" + def); - } - // Execute command - if (!this->RunCommand(cmd, output)) { - this->LogCommandError(cmQtAutoGen::MOC, - "moc_predefs generation failed", cmd, output); - return false; - } - } - - // (Re)write predefs file only on demand - if (this->FileDiffers(this->MocPredefsFileAbs, output)) { - if (this->FileWrite(cmQtAutoGen::MOC, this->MocPredefsFileAbs, - output)) { - this->MocPredefsChanged = true; - } else { - this->LogFileError(cmQtAutoGen::MOC, this->MocPredefsFileAbs, - "moc_predefs file writing failed"); - return false; - } - } else { - // Touch to update the time stamp - if (this->Verbose) { - this->LogInfo(cmQtAutoGen::MOC, - "Touching moc_predefs " + this->MocPredefsFileRel); - } - cmSystemTools::Touch(this->MocPredefsFileAbs, false); - } - } - - // Add moc_predefs.h to moc file dependencies - for (auto const& item : this->MocJobsIncluded) { - item->Depends.insert(this->MocPredefsFileAbs); - } - for (auto const& item : this->MocJobsAuto) { - item->Depends.insert(this->MocPredefsFileAbs); - } - } - - // Generate moc files that are included by source files. - for (auto const& item : this->MocJobsIncluded) { - if (!this->MocGenerateFile(*item)) { - return false; - } - } - // Generate moc files that are _not_ included by source files. - bool autoNameGenerated = false; - for (auto const& item : this->MocJobsAuto) { - if (!this->MocGenerateFile(*item, &autoNameGenerated)) { - return false; - } - } - - // Compose mocs compilation file content - { - std::string mocs = - "// This file is autogenerated. Changes will be overwritten.\n"; - if (this->MocJobsAuto.empty()) { - // Placeholder content - mocs += - "// No files found that require moc or the moc files are included\n"; - mocs += "enum some_compilers { need_more_than_nothing };\n"; - } else { - // Valid content - for (const auto& item : this->MocJobsAuto) { - mocs += "#include \""; - mocs += item->BuildFileRel; - mocs += "\"\n"; - } - } - - if (this->FileDiffers(this->MocCompFileAbs, mocs)) { - // Actually write mocs compilation file - if (this->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); - } - } - - return true; -} - -/** - * @return True on success - */ -bool cmQtAutoGenerators::MocGenerateFile(const MocJobAuto& mocJob, - bool* generated) -{ - bool success = true; - - std::string const mocFileAbs = cmSystemTools::CollapseCombinedPath( - this->AutogenBuildDir, mocJob.BuildFileRel); - - bool generate = false; - std::string generateReason; - if (!generate && !cmSystemTools::FileExists(mocFileAbs.c_str())) { - if (this->Verbose) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(mocFileAbs); - generateReason += " from its source file "; - generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile); - generateReason += " because it doesn't exist"; - } - generate = true; - } - if (!generate && this->MocSettingsChanged) { - if (this->Verbose) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(mocFileAbs); - generateReason += " from "; - generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile); - generateReason += " because the MOC settings changed"; - } - generate = true; - } - if (!generate && this->MocPredefsChanged) { - if (this->Verbose) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(mocFileAbs); - generateReason += " from "; - generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile); - generateReason += " because moc_predefs.h changed"; - } - generate = true; - } - if (!generate) { - std::string error; - if (FileIsOlderThan(mocFileAbs, mocJob.SourceFile, &error)) { - if (this->Verbose) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(mocFileAbs); - generateReason += " because it's older than its source file "; - generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile); - } - generate = true; - } else { - if (!error.empty()) { - this->LogError(cmQtAutoGen::MOC, error); - success = false; - } - } - } - if (success && !generate) { - // Test if a dependency file is newer - std::string error; - for (std::string const& depFile : mocJob.Depends) { - if (FileIsOlderThan(mocFileAbs, depFile, &error)) { - if (this->Verbose) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(mocFileAbs); - generateReason += " from "; - generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile); - generateReason += " because it is older than "; - generateReason += cmQtAutoGen::Quoted(depFile); - } - generate = true; - break; - } - if (!error.empty()) { - this->LogError(cmQtAutoGen::MOC, error); - success = false; - break; - } - } - } - - if (generate) { - // Log - if (this->Verbose) { - this->LogBold("Generating MOC source " + mocJob.BuildFileRel); - this->LogInfo(cmQtAutoGen::MOC, generateReason); - } - - // Make sure the parent directory exists - if (this->MakeParentDirectory(cmQtAutoGen::MOC, mocFileAbs)) { - // Compose moc command - std::vector cmd; - cmd.push_back(this->MocExecutable); - // Add options - cmd.insert(cmd.end(), this->MocAllOptions.begin(), - this->MocAllOptions.end()); - // Add predefs include - if (!this->MocPredefsFileAbs.empty()) { - cmd.push_back("--include"); - cmd.push_back(this->MocPredefsFileAbs); - } - cmd.push_back("-o"); - cmd.push_back(mocFileAbs); - cmd.push_back(mocJob.SourceFile); - - // Execute moc command - std::string output; - if (this->RunCommand(cmd, output)) { - // Success - if (generated != nullptr) { - *generated = true; - } - } else { - // Moc command failed - { - std::string emsg = "moc failed for\n "; - emsg += cmQtAutoGen::Quoted(mocJob.SourceFile); - this->LogCommandError(cmQtAutoGen::MOC, emsg, cmd, output); - } - cmSystemTools::RemoveFile(mocFileAbs); - success = false; - } - } else { - // Parent directory creation failed - success = false; - } - } - return success; -} - -/** - * @brief Tests if the file name is in the skip list - */ -bool 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 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(); - job->SourceFile = uiInputFile; - job->BuildFileRel = this->AutogenIncludeDir; - job->BuildFileRel += includeString; - job->Includer = absFilename; - job->IncludeString = includeString; - this->UicJobs.push_back(std::move(job)); - } - } - - return true; -} - -bool cmQtAutoGenerators::UicFindIncludedFile(std::string& absFile, - std::string const& sourceFile, - std::string const& includeString) -{ - bool success = false; - std::string searchFile = - cmSystemTools::GetFilenameWithoutLastExtension(includeString).substr(3); - searchFile += ".ui"; - // Collect search paths list - std::vector testFiles; - { - std::string const searchPath = SubDirPrefix(includeString); - - std::string searchFileFull; - if (!searchPath.empty()) { - searchFileFull = searchPath; - searchFileFull += searchFile; - } - // Vicinity of the source - { - std::string const sourcePath = SubDirPrefix(sourceFile); - testFiles.push_back(sourcePath + searchFile); - if (!searchPath.empty()) { - testFiles.push_back(sourcePath + searchFileFull); - } - } - // AUTOUIC search paths - if (!this->UicSearchPaths.empty()) { - for (std::string const& sPath : this->UicSearchPaths) { - testFiles.push_back((sPath + "/").append(searchFile)); - } - if (!searchPath.empty()) { - for (std::string const& sPath : this->UicSearchPaths) { - testFiles.push_back((sPath + "/").append(searchFileFull)); - } - } - } - } - - // Search for the .ui file! - for (std::string const& testFile : testFiles) { - if (cmSystemTools::FileExists(testFile.c_str())) { - absFile = cmSystemTools::GetRealPath(testFile); - success = true; - break; - } - } - - // Log error - if (!success) { - std::string emsg = "Could not find "; - emsg += cmQtAutoGen::Quoted(searchFile); - emsg += " in\n"; - for (std::string const& testFile : testFiles) { - emsg += " "; - emsg += cmQtAutoGen::Quoted(testFile); - emsg += "\n"; - } - this->LogFileError(cmQtAutoGen::UIC, sourceFile, emsg); - } - - return success; -} - -bool cmQtAutoGenerators::UicGenerateAll() -{ - if (!this->UicEnabled()) { - return true; - } - - // Look for name collisions in included uic files - { - bool collision = false; - std::map> collisions; - for (auto const& job : this->UicJobs) { - auto& list = collisions[job->IncludeString]; - if (!list.empty()) { - collision = true; - } - list.push_back(job.get()); - } - if (collision) { - std::string emsg = - "Included uic files with the same name will be " - "generated from different sources.\n" - "Consider to\n" - " - add a directory prefix to a \"ui_.h\" include " - "(e.g \"sub/ui_.h\")\n" - " - rename the .ui file(s) and adjust the \"ui_.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 header files - for (const auto& item : this->UicJobs) { - if (!this->UicGenerateFile(*item)) { - return false; - } - } - - return true; -} - -/** - * @return True on success - */ -bool cmQtAutoGenerators::UicGenerateFile(const UicJob& uicJob) -{ - bool success = true; - - std::string const uicFileAbs = cmSystemTools::CollapseCombinedPath( - this->AutogenBuildDir, uicJob.BuildFileRel); - - bool generate = false; - std::string generateReason; - if (!generate && !cmSystemTools::FileExists(uicFileAbs.c_str())) { - if (this->Verbose) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(uicFileAbs); - generateReason += " from its source file "; - generateReason += cmQtAutoGen::Quoted(uicJob.SourceFile); - generateReason += " because it doesn't exist"; - } - generate = true; - } - if (!generate && this->UicSettingsChanged) { - if (this->Verbose) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(uicFileAbs); - generateReason += " from "; - generateReason += cmQtAutoGen::Quoted(uicJob.SourceFile); - generateReason += " because the UIC settings changed"; - } - generate = true; - } - if (!generate) { - std::string error; - if (FileIsOlderThan(uicFileAbs, uicJob.SourceFile, &error)) { - if (this->Verbose) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(uicFileAbs); - generateReason += " because it's older than its source file "; - generateReason += cmQtAutoGen::Quoted(uicJob.SourceFile); - } - generate = true; - } else { - if (!error.empty()) { - this->LogError(cmQtAutoGen::UIC, error); - success = false; - } - } - } - if (generate) { - // Log - if (this->Verbose) { - this->LogBold("Generating UIC header " + uicJob.BuildFileRel); - this->LogInfo(cmQtAutoGen::UIC, generateReason); - } - - // Make sure the parent directory exists - if (this->MakeParentDirectory(cmQtAutoGen::UIC, uicFileAbs)) { - // Compose uic command - std::vector cmd; - cmd.push_back(this->UicExecutable); - { - std::vector allOpts = this->UicTargetOptions; - auto optionIt = this->UicOptions.find(uicJob.SourceFile); - if (optionIt != this->UicOptions.end()) { - cmQtAutoGen::UicMergeOptions(allOpts, optionIt->second, - (this->QtMajorVersion == "5")); - } - cmd.insert(cmd.end(), allOpts.begin(), allOpts.end()); - } - cmd.push_back("-o"); - cmd.push_back(uicFileAbs); - cmd.push_back(uicJob.SourceFile); - - std::string output; - if (this->RunCommand(cmd, output)) { - // Success - } else { - // Command failed - { - std::string emsg = "uic failed for\n "; - emsg += cmQtAutoGen::Quoted(uicJob.SourceFile); - emsg += "\nincluded by\n "; - emsg += cmQtAutoGen::Quoted(uicJob.Includer); - this->LogCommandError(cmQtAutoGen::UIC, emsg, cmd, output); - } - cmSystemTools::RemoveFile(uicFileAbs); - success = false; - } - } else { - // Parent directory creation failed - success = false; - } - } - return success; -} - -void cmQtAutoGenerators::LogBold(std::string const& message) const -{ - cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundBlue | - cmsysTerminal_Color_ForegroundBold, - message.c_str(), true, this->ColorOutput); -} - -void cmQtAutoGenerators::LogInfo(cmQtAutoGen::Generator genType, - std::string const& message) const -{ - std::string msg = cmQtAutoGen::GeneratorName(genType); - msg += ": "; - msg += message; - if (msg.back() != '\n') { - msg.push_back('\n'); - } - cmSystemTools::Stdout(msg.c_str(), msg.size()); -} - -void cmQtAutoGenerators::LogWarning(cmQtAutoGen::Generator genType, - std::string const& message) const -{ - std::string msg = cmQtAutoGen::GeneratorName(genType); - msg += " warning:"; - if (message.find('\n') == std::string::npos) { - // Single line message - msg.push_back(' '); - } else { - // Multi line message - msg.push_back('\n'); - } - // Message - msg += message; - if (msg.back() != '\n') { - msg.push_back('\n'); - } - msg.push_back('\n'); - cmSystemTools::Stdout(msg.c_str(), msg.size()); -} - -void cmQtAutoGenerators::LogFileWarning(cmQtAutoGen::Generator genType, - std::string const& filename, - std::string const& message) const -{ - std::string msg = " "; - msg += cmQtAutoGen::Quoted(filename); - msg.push_back('\n'); - // Message - msg += message; - this->LogWarning(genType, msg); -} - -void cmQtAutoGenerators::LogError(cmQtAutoGen::Generator genType, - std::string const& message) const -{ - std::string msg; - msg.push_back('\n'); - msg += HeadLine(cmQtAutoGen::GeneratorName(genType) + " error"); - // Message - msg += message; - if (msg.back() != '\n') { - msg.push_back('\n'); - } - msg.push_back('\n'); - cmSystemTools::Stderr(msg.c_str(), msg.size()); -} - -void cmQtAutoGenerators::LogFileError(cmQtAutoGen::Generator genType, - std::string const& filename, - std::string const& message) const -{ - std::string emsg = " "; - emsg += cmQtAutoGen::Quoted(filename); - emsg += '\n'; - // Message - emsg += message; - this->LogError(genType, emsg); -} - -void cmQtAutoGenerators::LogCommandError( - cmQtAutoGen::Generator genType, std::string const& message, - std::vector const& command, std::string const& output) const -{ - std::string msg; - msg.push_back('\n'); - msg += HeadLine(cmQtAutoGen::GeneratorName(genType) + " subprocess error"); - msg += message; - if (msg.back() != '\n') { - msg.push_back('\n'); - } - msg.push_back('\n'); - msg += HeadLine("Command"); - msg += QuotedCommand(command); - if (msg.back() != '\n') { - msg.push_back('\n'); - } - msg.push_back('\n'); - msg += HeadLine("Output"); - msg += output; - if (msg.back() != '\n') { - msg.push_back('\n'); - } - msg.push_back('\n'); - cmSystemTools::Stderr(msg.c_str(), msg.size()); -} - -/** - * @brief Generates the parent directory of the given file on demand - * @return True on success - */ -bool cmQtAutoGenerators::MakeParentDirectory(cmQtAutoGen::Generator genType, - std::string const& filename) const -{ - bool success = true; - std::string const dirName = cmSystemTools::GetFilenamePath(filename); - if (!dirName.empty()) { - if (!cmSystemTools::MakeDirectory(dirName)) { - this->LogFileError(genType, filename, - "Could not create parent directory"); - success = false; - } - } - return success; -} - -bool cmQtAutoGenerators::FileDiffers(std::string const& filename, - std::string const& content) -{ - bool differs = true; - { - std::string oldContents; - if (ReadFile(oldContents, filename)) { - differs = (oldContents != content); - } - } - return differs; -} - -bool cmQtAutoGenerators::FileWrite(cmQtAutoGen::Generator genType, - 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::out | std::ios::binary | std::ios::trunc)); - if (outfile) { - outfile << content; - // Check for write errors - if (!outfile.good()) { - error = "File writing failed"; - } - } else { - error = "Opening file for writing failed"; - } - } - if (!error.empty()) { - this->LogFileError(genType, filename, error); - return false; - } - return true; -} - -/** - * @brief Runs a command and returns true on success - * @return True on success - */ -bool cmQtAutoGenerators::RunCommand(std::vector const& command, - std::string& output) const -{ - // Log command - if (this->Verbose) { - std::string qcmd = QuotedCommand(command); - qcmd.push_back('\n'); - cmSystemTools::Stdout(qcmd.c_str(), qcmd.size()); - } - // Execute command - int retVal = 0; - bool res = cmSystemTools::RunSingleCommand( - command, &output, &output, &retVal, nullptr, cmSystemTools::OUTPUT_NONE); - return (res && (retVal == 0)); -} - -/** - * @brief Tries to find the header file to the given file base path by - * appending different header extensions - * @return True on success - */ -bool cmQtAutoGenerators::FindHeader(std::string& header, - std::string const& testBasePath) const -{ - for (std::string const& ext : this->HeaderExtensions) { - std::string testFilePath(testBasePath); - testFilePath.push_back('.'); - testFilePath += ext; - if (cmSystemTools::FileExists(testFilePath.c_str())) { - header = testFilePath; - return true; - } - } - return false; -} diff --git a/Source/cmQtAutoGenerators.h b/Source/cmQtAutoGenerators.h deleted file mode 100644 index b6e28b3..0000000 --- a/Source/cmQtAutoGenerators.h +++ /dev/null @@ -1,228 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmQtAutoGenerators_h -#define cmQtAutoGenerators_h - -#include "cmConfigure.h" // IWYU pragma: keep - -#include "cmFilePathChecksum.h" -#include "cmQtAutoGen.h" -#include "cmsys/RegularExpression.hxx" - -#include -#include // IWYU pragma: keep -#include -#include -#include - -class cmMakefile; - -class cmQtAutoGenerators -{ - CM_DISABLE_COPY(cmQtAutoGenerators) -public: - cmQtAutoGenerators(); - bool Run(std::string const& targetDirectory, std::string const& config); - -private: - // -- Types - - /// @brief Search key plus regular expression pair - struct KeyRegExp - { - KeyRegExp() = default; - - KeyRegExp(const char* key, const char* regExp) - : Key(key) - , RegExp(regExp) - { - } - - KeyRegExp(std::string const& key, std::string const& regExp) - : Key(key) - , RegExp(regExp) - { - } - - std::string Key; - 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 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; - }; - - // -- Initialization - bool InitInfoFile(cmMakefile* makefile, std::string const& targetDirectory, - std::string const& config); - - // -- Settings file - void SettingsFileRead(cmMakefile* makefile); - bool SettingsFileWrite(); - bool SettingsChanged() const - { - return (this->MocSettingsChanged || this->UicSettingsChanged); - } - - // -- Central processing - bool Process(); - - // -- Source parsing - bool ParseSourceFile(std::string const& absFilename, const SourceJob& job); - bool ParseHeaderFile(std::string const& absFilename, const SourceJob& job); - bool ParsePostprocess(); - - // -- Moc - bool MocEnabled() const { return !this->MocExecutable.empty(); } - bool MocSkip(std::string const& absFilename) const; - bool MocRequired(std::string const& contentText, - std::string* macroName = nullptr); - // Moc strings - std::string MocStringMacros() const; - std::string MocStringHeaders(std::string const& fileBase) const; - std::string MocFindIncludedHeader(std::string const& sourcePath, - std::string const& includeBase) const; - bool MocFindIncludedFile(std::string& absFile, std::string const& sourceFile, - std::string const& includeString) const; - // Moc depends - bool MocDependFilterPush(std::string const& key, std::string const& regExp); - void MocFindDepends(std::string const& absFilename, - std::string const& contentText, - std::set& depends); - // Moc - bool MocParseSourceContent(std::string const& absFilename, - std::string const& contentText); - void MocParseHeaderContent(std::string const& absFilename, - std::string const& contentText); - - bool MocGenerateAll(); - bool MocGenerateFile(const MocJobAuto& mocJob, bool* generated = nullptr); - - // -- Uic - bool UicEnabled() const { return !this->UicExecutable.empty(); } - bool UicSkip(std::string const& absFilename) const; - bool UicParseContent(std::string const& fileName, - std::string const& contentText); - bool UicFindIncludedFile(std::string& absFile, std::string const& sourceFile, - std::string const& includeString); - bool UicGenerateAll(); - bool UicGenerateFile(const UicJob& uicJob); - - // -- Log info - void LogBold(std::string const& message) const; - void LogInfo(cmQtAutoGen::Generator genType, - std::string const& message) const; - // -- Log warning - void LogWarning(cmQtAutoGen::Generator genType, - std::string const& message) const; - void LogFileWarning(cmQtAutoGen::Generator genType, - std::string const& filename, - std::string const& message) const; - // -- Log error - void LogError(cmQtAutoGen::Generator genType, - std::string const& message) const; - void LogFileError(cmQtAutoGen::Generator genType, - std::string const& filename, - std::string const& message) const; - void LogCommandError(cmQtAutoGen::Generator genType, - std::string const& message, - std::vector const& command, - std::string const& output) const; - - // -- Utility - bool MakeParentDirectory(cmQtAutoGen::Generator genType, - std::string const& filename) const; - bool FileDiffers(std::string const& filename, std::string const& content); - bool FileWrite(cmQtAutoGen::Generator genType, std::string const& filename, - std::string const& content); - bool FindHeader(std::string& header, std::string const& testBasePath) const; - bool RunCommand(std::vector const& command, - std::string& output) const; - - // -- Meta - std::string InfoFile; - std::string ConfigSuffix; - cmQtAutoGen::MultiConfig MultiConfig; - // -- Settings - bool IncludeProjectDirsBefore; - bool Verbose; - bool ColorOutput; - std::string SettingsFile; - std::string SettingsStringMoc; - std::string SettingsStringUic; - // -- Directories - std::string ProjectSourceDir; - std::string ProjectBinaryDir; - std::string CurrentSourceDir; - std::string CurrentBinaryDir; - std::string AutogenBuildDir; - std::string AutogenIncludeDir; - // -- Qt environment - std::string QtMajorVersion; - std::string QtMinorVersion; - std::string MocExecutable; - std::string UicExecutable; - // -- File lists - std::map HeaderJobs; - std::map SourceJobs; - std::vector HeaderExtensions; - cmFilePathChecksum FilePathChecksum; - // -- Moc - bool MocSettingsChanged; - bool MocPredefsChanged; - bool MocRelaxedMode; - std::string MocCompFileRel; - std::string MocCompFileAbs; - std::string MocPredefsFileRel; - std::string MocPredefsFileAbs; - std::vector MocSkipList; - std::vector MocIncludePaths; - std::vector MocIncludes; - std::vector MocDefinitions; - std::vector MocOptions; - std::vector MocAllOptions; - std::vector MocPredefsCmd; - std::vector MocDependFilters; - std::vector MocMacroFilters; - cmsys::RegularExpression MocRegExpInclude; - std::vector> MocJobsIncluded; - std::vector> MocJobsAuto; - // -- Uic - bool UicSettingsChanged; - std::vector UicSkipList; - std::vector UicTargetOptions; - std::map> UicOptions; - std::vector UicSearchPaths; - cmsys::RegularExpression UicRegExpInclude; - std::vector> UicJobs; -}; - -#endif diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx index a37bc3c..3d9f65a 100644 --- a/Source/cmcmd.cxx +++ b/Source/cmcmd.cxx @@ -6,8 +6,8 @@ #include "cmGlobalGenerator.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" +#include "cmQtAutoGeneratorMocUic.h" #include "cmQtAutoGeneratorRcc.h" -#include "cmQtAutoGenerators.h" #include "cmStateDirectory.h" #include "cmStateSnapshot.h" #include "cmSystemTools.h" @@ -994,7 +994,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector& args) #ifdef CMAKE_BUILD_WITH_CMAKE if ((args[1] == "cmake_autogen") && (args.size() >= 4)) { - cmQtAutoGenerators autoGen; + cmQtAutoGeneratorMocUic autoGen; std::string const& infoDir = args[2]; std::string const& config = args[3]; return autoGen.Run(infoDir, config) ? 0 : 1; -- cgit v0.12