| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
| file Copyright.txt or https://cmake.org/licensing for details. */ |
| #include "cmQtAutoMocUic.h" |
| |
| #include <algorithm> |
| #include <array> |
| #include <list> |
| #include <memory> |
| #include <set> |
| #include <sstream> |
| #include <utility> |
| |
| #include "cmAlgorithms.h" |
| #include "cmCryptoHash.h" |
| #include "cmGeneratedFileStream.h" |
| #include "cmMakefile.h" |
| #include "cmQtAutoGen.h" |
| #include "cmSystemTools.h" |
| #include "cmake.h" |
| #include "cmsys/FStream.hxx" |
| |
| #if defined(__APPLE__) |
| # include <unistd.h> |
| #endif |
| |
| static constexpr std::size_t MocUnderscoreLength = 4; // Length of "moc_" |
| static constexpr std::size_t UiUnderscoreLength = 3; // Length of "ui_" |
| |
| cmQtAutoMocUic::IncludeKeyT::IncludeKeyT(std::string const& key, |
| std::size_t basePrefixLength) |
| : Key(key) |
| , Dir(SubDirPrefix(key)) |
| , Base(cmSystemTools::GetFilenameWithoutLastExtension(key)) |
| { |
| if (basePrefixLength != 0) { |
| Base = Base.substr(basePrefixLength); |
| } |
| } |
| |
| void cmQtAutoMocUic::ParseCacheT::FileT::Clear() |
| { |
| Moc.Macro.clear(); |
| Moc.Include.Underscore.clear(); |
| Moc.Include.Dot.clear(); |
| Moc.Depends.clear(); |
| |
| Uic.Include.clear(); |
| Uic.Depends.clear(); |
| } |
| |
| cmQtAutoMocUic::ParseCacheT::FileHandleT cmQtAutoMocUic::ParseCacheT::Get( |
| std::string const& fileName) const |
| { |
| auto it = Map_.find(fileName); |
| if (it != Map_.end()) { |
| return it->second; |
| } |
| return FileHandleT(); |
| } |
| |
| cmQtAutoMocUic::ParseCacheT::GetOrInsertT |
| cmQtAutoMocUic::ParseCacheT::GetOrInsert(std::string const& fileName) |
| { |
| // Find existing entry |
| { |
| auto it = Map_.find(fileName); |
| if (it != Map_.end()) { |
| return GetOrInsertT{ it->second, false }; |
| } |
| } |
| |
| // Insert new entry |
| return GetOrInsertT{ |
| Map_.emplace(fileName, std::make_shared<FileT>()).first->second, true |
| }; |
| } |
| |
| cmQtAutoMocUic::ParseCacheT::ParseCacheT() = default; |
| cmQtAutoMocUic::ParseCacheT::~ParseCacheT() = default; |
| |
| void cmQtAutoMocUic::ParseCacheT::Clear() |
| { |
| Map_.clear(); |
| } |
| |
| bool cmQtAutoMocUic::ParseCacheT::ReadFromFile(std::string const& fileName) |
| { |
| cmsys::ifstream fin(fileName.c_str()); |
| if (!fin) { |
| return false; |
| } |
| FileHandleT fileHandle; |
| |
| std::string line; |
| while (std::getline(fin, line)) { |
| // Check if this an empty or a comment line |
| if (line.empty() || line.front() == '#') { |
| continue; |
| } |
| // Drop carriage return character at the end |
| if (line.back() == '\r') { |
| line.pop_back(); |
| if (line.empty()) { |
| continue; |
| } |
| } |
| // Check if this a file name line |
| if (line.front() != ' ') { |
| fileHandle = GetOrInsert(line).first; |
| continue; |
| } |
| |
| // Bad line or bad file handle |
| if (!fileHandle || (line.size() < 6)) { |
| continue; |
| } |
| |
| constexpr std::size_t offset = 5; |
| if (cmHasLiteralPrefix(line, " mmc:")) { |
| fileHandle->Moc.Macro = line.substr(offset); |
| continue; |
| } |
| if (cmHasLiteralPrefix(line, " miu:")) { |
| fileHandle->Moc.Include.Underscore.emplace_back(line.substr(offset), |
| MocUnderscoreLength); |
| continue; |
| } |
| if (cmHasLiteralPrefix(line, " mid:")) { |
| fileHandle->Moc.Include.Dot.emplace_back(line.substr(offset), 0); |
| continue; |
| } |
| if (cmHasLiteralPrefix(line, " mdp:")) { |
| fileHandle->Moc.Depends.emplace_back(line.substr(offset)); |
| continue; |
| } |
| if (cmHasLiteralPrefix(line, " uic:")) { |
| fileHandle->Uic.Include.emplace_back(line.substr(offset), |
| UiUnderscoreLength); |
| continue; |
| } |
| if (cmHasLiteralPrefix(line, " udp:")) { |
| fileHandle->Uic.Depends.emplace_back(line.substr(offset)); |
| continue; |
| } |
| } |
| return true; |
| } |
| |
| bool cmQtAutoMocUic::ParseCacheT::WriteToFile(std::string const& fileName) |
| { |
| cmGeneratedFileStream ofs(fileName); |
| if (!ofs) { |
| return false; |
| } |
| ofs << "# Generated by CMake. Changes will be overwritten." << std::endl; |
| for (auto const& pair : Map_) { |
| ofs << pair.first << std::endl; |
| FileT const& file = *pair.second; |
| if (!file.Moc.Macro.empty()) { |
| ofs << " mmc:" << file.Moc.Macro << std::endl; |
| } |
| for (IncludeKeyT const& item : file.Moc.Include.Underscore) { |
| ofs << " miu:" << item.Key << std::endl; |
| } |
| for (IncludeKeyT const& item : file.Moc.Include.Dot) { |
| ofs << " mid:" << item.Key << std::endl; |
| } |
| for (std::string const& item : file.Moc.Depends) { |
| ofs << " mdp:" << item << std::endl; |
| } |
| for (IncludeKeyT const& item : file.Uic.Include) { |
| ofs << " uic:" << item.Key << std::endl; |
| } |
| for (std::string const& item : file.Uic.Depends) { |
| ofs << " udp:" << item << std::endl; |
| } |
| } |
| return ofs.Close(); |
| } |
| |
| cmQtAutoMocUic::BaseSettingsT::BaseSettingsT() = default; |
| cmQtAutoMocUic::BaseSettingsT::~BaseSettingsT() = default; |
| |
| cmQtAutoMocUic::MocSettingsT::MocSettingsT() |
| { |
| RegExpInclude.compile( |
| "(^|\n)[ \t]*#[ \t]*include[ \t]+" |
| "[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]"); |
| } |
| |
| cmQtAutoMocUic::MocSettingsT::~MocSettingsT() = default; |
| |
| bool cmQtAutoMocUic::MocSettingsT::skipped(std::string const& fileName) const |
| { |
| return (!Enabled || (SkipList.find(fileName) != SkipList.end())); |
| } |
| |
| std::string cmQtAutoMocUic::MocSettingsT::MacrosString() const |
| { |
| std::string res; |
| const auto itB = MacroFilters.cbegin(); |
| const auto itE = MacroFilters.cend(); |
| const auto itL = itE - 1; |
| auto itC = itB; |
| for (; itC != itE; ++itC) { |
| // Separator |
| if (itC != itB) { |
| if (itC != itL) { |
| res += ", "; |
| } else { |
| res += " or "; |
| } |
| } |
| // Key |
| res += itC->Key; |
| } |
| return res; |
| } |
| |
| cmQtAutoMocUic::UicSettingsT::UicSettingsT() |
| { |
| RegExpInclude.compile("(^|\n)[ \t]*#[ \t]*include[ \t]+" |
| "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]"); |
| } |
| |
| cmQtAutoMocUic::UicSettingsT::~UicSettingsT() = default; |
| |
| bool cmQtAutoMocUic::UicSettingsT::skipped(std::string const& fileName) const |
| { |
| return (!Enabled || (SkipList.find(fileName) != SkipList.end())); |
| } |
| |
| void cmQtAutoMocUic::JobT::LogError(GenT genType, |
| std::string const& message) const |
| { |
| Gen()->AbortError(); |
| Gen()->Log().Error(genType, message); |
| } |
| |
| void cmQtAutoMocUic::JobT::LogFileError(GenT genType, |
| std::string const& filename, |
| std::string const& message) const |
| { |
| Gen()->AbortError(); |
| Gen()->Log().ErrorFile(genType, filename, message); |
| } |
| |
| void cmQtAutoMocUic::JobT::LogCommandError( |
| GenT genType, std::string const& message, |
| std::vector<std::string> const& command, std::string const& output) const |
| { |
| Gen()->AbortError(); |
| Gen()->Log().ErrorCommand(genType, message, command, output); |
| } |
| |
| bool cmQtAutoMocUic::JobT::RunProcess(GenT genType, |
| cmWorkerPool::ProcessResultT& result, |
| std::vector<std::string> const& command, |
| std::string* infoMessage) |
| { |
| // Log command |
| if (Log().Verbose()) { |
| std::string msg; |
| if ((infoMessage != nullptr) && !infoMessage->empty()) { |
| msg = *infoMessage; |
| if (msg.back() != '\n') { |
| msg += '\n'; |
| } |
| } |
| msg += QuotedCommand(command); |
| msg += '\n'; |
| Log().Info(genType, msg); |
| } |
| return cmWorkerPool::JobT::RunProcess(result, command, |
| BaseConst().AutogenBuildDir); |
| } |
| |
| void cmQtAutoMocUic::JobMocPredefsT::Process() |
| { |
| // (Re)generate moc_predefs.h on demand |
| std::unique_ptr<std::string> reason; |
| if (Log().Verbose()) { |
| reason = cm::make_unique<std::string>(); |
| } |
| if (!Update(reason.get())) { |
| return; |
| } |
| std::string const& predefsFileRel = MocConst().PredefsFileRel; |
| std::string const& predefsFileAbs = MocConst().PredefsFileAbs; |
| { |
| cmWorkerPool::ProcessResultT result; |
| { |
| // Compose command |
| std::vector<std::string> cmd = MocConst().PredefsCmd; |
| // Add includes |
| cmAppend(cmd, MocConst().Includes); |
| // Add definitions |
| for (std::string const& def : MocConst().Definitions) { |
| cmd.emplace_back("-D" + def); |
| } |
| // Execute command |
| if (!RunProcess(GenT::MOC, result, cmd, reason.get())) { |
| std::string msg = "The content generation command for "; |
| msg += Quoted(predefsFileRel); |
| msg += " failed.\n"; |
| msg += result.ErrorMessage; |
| LogCommandError(GenT::MOC, msg, cmd, result.StdOut); |
| return; |
| } |
| } |
| |
| // (Re)write predefs file only on demand |
| if (cmQtAutoGenerator::FileDiffers(predefsFileAbs, result.StdOut)) { |
| if (!cmQtAutoGenerator::FileWrite(predefsFileAbs, result.StdOut)) { |
| std::string msg = "Writing "; |
| msg += Quoted(predefsFileRel); |
| msg += " failed."; |
| LogFileError(GenT::MOC, predefsFileAbs, msg); |
| return; |
| } |
| } else { |
| // Touch to update the time stamp |
| if (Log().Verbose()) { |
| Log().Info(GenT::MOC, "Touching " + Quoted(predefsFileRel)); |
| } |
| if (!cmSystemTools::Touch(predefsFileAbs, false)) { |
| std::string msg = "Touching "; |
| msg += Quoted(predefsFileAbs); |
| msg += " failed."; |
| LogFileError(GenT::MOC, predefsFileAbs, msg); |
| return; |
| } |
| } |
| } |
| |
| // Read file time afterwards |
| if (!MocEval().PredefsTime.Load(predefsFileAbs)) { |
| LogFileError(GenT::MOC, predefsFileAbs, "File time reading failed."); |
| return; |
| } |
| } |
| |
| bool cmQtAutoMocUic::JobMocPredefsT::Update(std::string* reason) const |
| { |
| // Test if the file exists |
| if (!MocEval().PredefsTime.Load(MocConst().PredefsFileAbs)) { |
| if (reason != nullptr) { |
| *reason = "Generating "; |
| *reason += Quoted(MocConst().PredefsFileRel); |
| *reason += ", because it doesn't exist."; |
| } |
| return true; |
| } |
| |
| // Test if the settings changed |
| if (MocConst().SettingsChanged) { |
| if (reason != nullptr) { |
| *reason = "Generating "; |
| *reason += Quoted(MocConst().PredefsFileRel); |
| *reason += ", because the moc settings changed."; |
| } |
| return true; |
| } |
| |
| // Test if the executable is newer |
| { |
| std::string const& exec = MocConst().PredefsCmd.at(0); |
| cmFileTime execTime; |
| if (execTime.Load(exec)) { |
| if (MocEval().PredefsTime.Older(execTime)) { |
| if (reason != nullptr) { |
| *reason = "Generating "; |
| *reason += Quoted(MocConst().PredefsFileRel); |
| *reason += " because it is older than "; |
| *reason += Quoted(exec); |
| *reason += "."; |
| } |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| bool cmQtAutoMocUic::JobParseT::ReadFile() |
| { |
| // Clear old parse information |
| FileHandle->ParseData->Clear(); |
| std::string const& fileName = FileHandle->FileName; |
| // Write info |
| if (Log().Verbose()) { |
| Log().Info(GenT::GEN, "Parsing " + Quoted(fileName)); |
| } |
| // Read file content |
| { |
| std::string error; |
| if (!cmQtAutoGenerator::FileRead(Content, fileName, &error)) { |
| LogFileError(GenT::GEN, fileName, "Could not read the file: " + error); |
| return false; |
| } |
| } |
| // Warn if empty |
| if (Content.empty()) { |
| Log().WarningFile(GenT::GEN, fileName, "The file is empty."); |
| return false; |
| } |
| return true; |
| } |
| |
| void cmQtAutoMocUic::JobParseT::CreateKeys(std::vector<IncludeKeyT>& container, |
| std::set<std::string> const& source, |
| std::size_t basePrefixLength) |
| { |
| if (source.empty()) { |
| return; |
| } |
| container.reserve(source.size()); |
| for (std::string const& src : source) { |
| container.emplace_back(src, basePrefixLength); |
| } |
| } |
| |
| void cmQtAutoMocUic::JobParseT::MocMacro() |
| { |
| for (KeyExpT const& filter : MocConst().MacroFilters) { |
| // Run a simple find string check |
| if (Content.find(filter.Key) == std::string::npos) { |
| continue; |
| } |
| // Run the expensive regular expression check loop |
| cmsys::RegularExpressionMatch match; |
| if (filter.Exp.find(Content.c_str(), match)) { |
| // Keep detected macro name |
| FileHandle->ParseData->Moc.Macro = filter.Key; |
| return; |
| } |
| } |
| } |
| |
| void cmQtAutoMocUic::JobParseT::MocDependecies() |
| { |
| if (MocConst().DependFilters.empty()) { |
| return; |
| } |
| |
| // Find dependency strings |
| std::set<std::string> parseDepends; |
| for (KeyExpT const& filter : MocConst().DependFilters) { |
| // Run a simple find string check |
| if (Content.find(filter.Key) == std::string::npos) { |
| continue; |
| } |
| // Run the expensive regular expression check loop |
| const char* contentChars = Content.c_str(); |
| cmsys::RegularExpressionMatch match; |
| while (filter.Exp.find(contentChars, match)) { |
| { |
| std::string dep = match.match(1); |
| if (!dep.empty()) { |
| parseDepends.emplace(std::move(dep)); |
| } |
| } |
| contentChars += match.end(); |
| } |
| } |
| |
| // Store dependency strings |
| { |
| auto& Depends = FileHandle->ParseData->Moc.Depends; |
| Depends.reserve(parseDepends.size()); |
| for (std::string const& item : parseDepends) { |
| Depends.emplace_back(item); |
| // Replace end of line characters in filenames |
| std::string& path = Depends.back(); |
| std::replace(path.begin(), path.end(), '\n', ' '); |
| std::replace(path.begin(), path.end(), '\r', ' '); |
| } |
| } |
| } |
| |
| void cmQtAutoMocUic::JobParseT::MocIncludes() |
| { |
| if (Content.find("moc") == std::string::npos) { |
| return; |
| } |
| |
| std::set<std::string> underscore; |
| std::set<std::string> dot; |
| { |
| const char* contentChars = Content.c_str(); |
| cmsys::RegularExpression const& regExp = MocConst().RegExpInclude; |
| cmsys::RegularExpressionMatch match; |
| while (regExp.find(contentChars, match)) { |
| std::string incString = match.match(2); |
| std::string const incBase = |
| cmSystemTools::GetFilenameWithoutLastExtension(incString); |
| if (cmHasLiteralPrefix(incBase, "moc_")) { |
| // moc_<BASE>.cpp |
| // Remove the moc_ part from the base name |
| underscore.emplace(std::move(incString)); |
| } else { |
| // <BASE>.moc |
| dot.emplace(std::move(incString)); |
| } |
| // Forward content pointer |
| contentChars += match.end(); |
| } |
| } |
| auto& Include = FileHandle->ParseData->Moc.Include; |
| CreateKeys(Include.Underscore, underscore, MocUnderscoreLength); |
| CreateKeys(Include.Dot, dot, 0); |
| } |
| |
| void cmQtAutoMocUic::JobParseT::UicIncludes() |
| { |
| if (Content.find("ui_") == std::string::npos) { |
| return; |
| } |
| |
| std::set<std::string> includes; |
| { |
| const char* contentChars = Content.c_str(); |
| cmsys::RegularExpression const& regExp = UicConst().RegExpInclude; |
| cmsys::RegularExpressionMatch match; |
| while (regExp.find(contentChars, match)) { |
| includes.emplace(match.match(2)); |
| // Forward content pointer |
| contentChars += match.end(); |
| } |
| } |
| CreateKeys(FileHandle->ParseData->Uic.Include, includes, UiUnderscoreLength); |
| } |
| |
| void cmQtAutoMocUic::JobParseHeaderT::Process() |
| { |
| if (!ReadFile()) { |
| return; |
| } |
| // Moc parsing |
| if (FileHandle->Moc) { |
| MocMacro(); |
| MocDependecies(); |
| } |
| // Uic parsing |
| if (FileHandle->Uic) { |
| UicIncludes(); |
| } |
| } |
| |
| void cmQtAutoMocUic::JobParseSourceT::Process() |
| { |
| if (!ReadFile()) { |
| return; |
| } |
| // Moc parsing |
| if (FileHandle->Moc) { |
| MocMacro(); |
| MocDependecies(); |
| MocIncludes(); |
| } |
| // Uic parsing |
| if (FileHandle->Uic) { |
| UicIncludes(); |
| } |
| } |
| |
| void cmQtAutoMocUic::JobEvaluateT::Process() |
| { |
| // Evaluate for moc |
| if (MocConst().Enabled) { |
| // Evaluate headers |
| for (auto const& pair : BaseEval().Headers) { |
| if (!MocEvalHeader(pair.second)) { |
| return; |
| } |
| } |
| // Evaluate sources |
| for (auto const& pair : BaseEval().Sources) { |
| if (!MocEvalSource(pair.second)) { |
| return; |
| } |
| } |
| } |
| // Evaluate for uic |
| if (UicConst().Enabled) { |
| if (!UicEval(BaseEval().Headers) || !UicEval(BaseEval().Sources)) { |
| return; |
| } |
| } |
| |
| // Add discovered header parse jobs |
| Gen()->CreateParseJobs<JobParseHeaderT>(MocEval().HeadersDiscovered); |
| // Add generate job after |
| Gen()->WorkerPool().EmplaceJob<JobGenerateT>(); |
| } |
| |
| bool cmQtAutoMocUic::JobEvaluateT::MocEvalHeader(SourceFileHandleT source) |
| { |
| SourceFileT const& sourceFile = *source; |
| auto const& parseData = sourceFile.ParseData->Moc; |
| if (!source->Moc) { |
| return true; |
| } |
| |
| if (!parseData.Macro.empty()) { |
| // Create a new mapping |
| MappingHandleT handle = std::make_shared<MappingT>(); |
| handle->SourceFile = std::move(source); |
| |
| // Absolute build path |
| if (BaseConst().MultiConfig) { |
| handle->OutputFile = Gen()->AbsoluteIncludePath(sourceFile.BuildPath); |
| } else { |
| handle->OutputFile = Gen()->AbsoluteBuildPath(sourceFile.BuildPath); |
| } |
| |
| // Register mapping in headers map |
| MocRegisterMapping(handle, true); |
| } |
| |
| return true; |
| } |
| |
| bool cmQtAutoMocUic::JobEvaluateT::MocEvalSource( |
| SourceFileHandleT const& source) |
| { |
| SourceFileT const& sourceFile = *source; |
| auto const& parseData = sourceFile.ParseData->Moc; |
| if (!sourceFile.Moc || |
| (parseData.Macro.empty() && parseData.Include.Underscore.empty() && |
| parseData.Include.Dot.empty())) { |
| return true; |
| } |
| |
| std::string const sourceDir = SubDirPrefix(sourceFile.FileName); |
| std::string const sourceBase = |
| cmSystemTools::GetFilenameWithoutLastExtension(sourceFile.FileName); |
| |
| // For relaxed mode check if the own "moc_" or ".moc" file is included |
| bool const relaxedMode = MocConst().RelaxedMode; |
| bool sourceIncludesMocUnderscore = false; |
| bool sourceIncludesDotMoc = false; |
| // Check if the sources own "moc_" or ".moc" file is included |
| if (relaxedMode) { |
| for (IncludeKeyT const& incKey : parseData.Include.Underscore) { |
| if (incKey.Base == sourceBase) { |
| sourceIncludesMocUnderscore = true; |
| break; |
| } |
| } |
| } |
| for (IncludeKeyT const& incKey : parseData.Include.Dot) { |
| if (incKey.Base == sourceBase) { |
| sourceIncludesDotMoc = true; |
| break; |
| } |
| } |
| |
| // Check if this source needs to be moc processed but doesn't. |
| if (!sourceIncludesDotMoc && !parseData.Macro.empty() && |
| !(relaxedMode && sourceIncludesMocUnderscore)) { |
| { |
| std::string emsg = "The file contains a "; |
| emsg += Quoted(parseData.Macro); |
| emsg += " macro, but does not include "; |
| emsg += Quoted(sourceBase + ".moc"); |
| emsg += "!\nConsider to\n - add #include \""; |
| emsg += sourceBase; |
| emsg += ".moc\"\n - enable SKIP_AUTOMOC for this file"; |
| LogFileError(GenT::MOC, sourceFile.FileName, emsg); |
| } |
| return false; |
| } |
| |
| // Evaluate "moc_" includes |
| for (IncludeKeyT const& incKey : parseData.Include.Underscore) { |
| std::string const headerBase = incKey.Dir + incKey.Base; |
| SourceFileHandleT header = MocFindIncludedHeader(sourceDir, headerBase); |
| if (!header) { |
| { |
| std::string msg = "The file includes the moc file "; |
| msg += Quoted(incKey.Key); |
| msg += ",\nbut the header could not be found " |
| "in the following locations\n"; |
| msg += MocMessageTestHeaders(headerBase); |
| LogFileError(GenT::MOC, sourceFile.FileName, msg); |
| } |
| return false; |
| } |
| // The include might be handled differently in relaxed mode |
| if (relaxedMode && !sourceIncludesDotMoc && !parseData.Macro.empty() && |
| (incKey.Base == sourceBase)) { |
| // The <BASE>.cpp file includes a Qt macro but does not include the |
| // <BASE>.moc file. In this case, the moc_<BASE>.cpp should probably |
| // be generated from <BASE>.cpp instead of <BASE>.h, because otherwise |
| // it won't build. But warn, since this is not how it is supposed to be |
| // used. This is for KDE4 compatibility. |
| { |
| // Issue a warning |
| std::string msg = "The file contains a "; |
| msg += Quoted(parseData.Macro); |
| msg += " macro, but does not include "; |
| msg += Quoted(sourceBase + ".moc"); |
| msg += ".\nInstead it includes "; |
| msg += Quoted(incKey.Key); |
| msg += ".\nRunning moc on the source\n "; |
| msg += Quoted(sourceFile.FileName); |
| msg += "!\nBetter include "; |
| msg += Quoted(sourceBase + ".moc"); |
| msg += " for compatibility with regular mode.\n"; |
| msg += "This is a CMAKE_AUTOMOC_RELAXED_MODE warning.\n"; |
| Log().WarningFile(GenT::MOC, sourceFile.FileName, msg); |
| } |
| // Create mapping |
| if (!MocRegisterIncluded(incKey.Key, source, source, false)) { |
| return false; |
| } |
| continue; |
| } |
| |
| // Check if header is skipped |
| if (MocConst().skipped(header->FileName)) { |
| continue; |
| } |
| // Create mapping |
| if (!MocRegisterIncluded(incKey.Key, source, std::move(header), true)) { |
| return false; |
| } |
| } |
| |
| // Evaluate ".moc" includes |
| if (relaxedMode) { |
| // Relaxed mode |
| for (IncludeKeyT const& incKey : parseData.Include.Dot) { |
| // Check if this is the sources own .moc file |
| bool const ownMoc = (incKey.Base == sourceBase); |
| if (ownMoc && !parseData.Macro.empty()) { |
| // Create mapping for the regular use case |
| if (!MocRegisterIncluded(incKey.Key, source, source, false)) { |
| return false; |
| } |
| continue; |
| } |
| // Try to find a header instead but issue a warning. |
| // This is for KDE4 compatibility. |
| std::string const headerBase = incKey.Dir + incKey.Base; |
| SourceFileHandleT header = MocFindIncludedHeader(sourceDir, headerBase); |
| if (!header) { |
| std::string msg = "The file includes the moc file "; |
| msg += Quoted(incKey.Key); |
| msg += ",\nwhich seems to be the moc file from a different source " |
| "file.\nCMAKE_AUTOMOC_RELAXED_MODE: Also a matching header" |
| "could not be found in the following locations\n"; |
| msg += MocMessageTestHeaders(headerBase); |
| LogFileError(GenT::MOC, sourceFile.FileName, msg); |
| return false; |
| } |
| // Check if header is skipped |
| if (MocConst().skipped(header->FileName)) { |
| continue; |
| } |
| // Issue a warning |
| if (ownMoc && parseData.Macro.empty()) { |
| std::string msg = "The file includes the moc file "; |
| msg += Quoted(incKey.Key); |
| msg += ", but does not contain a\n"; |
| msg += MocConst().MacrosString(); |
| msg += " macro.\nRunning moc on the header\n "; |
| msg += Quoted(header->FileName); |
| msg += "!\nBetter include "; |
| msg += Quoted("moc_" + incKey.Base + ".cpp"); |
| msg += " for a compatibility with regular mode.\n"; |
| msg += "This is a CMAKE_AUTOMOC_RELAXED_MODE warning.\n"; |
| Log().WarningFile(GenT::MOC, sourceFile.FileName, msg); |
| } else { |
| std::string msg = "The file includes the moc file "; |
| msg += Quoted(incKey.Key); |
| msg += " instead of "; |
| msg += Quoted("moc_" + incKey.Base + ".cpp"); |
| msg += ".\nRunning moc on the header\n "; |
| msg += Quoted(header->FileName); |
| msg += "!\nBetter include "; |
| msg += Quoted("moc_" + incKey.Base + ".cpp"); |
| msg += " for compatibility with regular mode.\n"; |
| msg += "This is a CMAKE_AUTOMOC_RELAXED_MODE warning.\n"; |
| Log().WarningFile(GenT::MOC, sourceFile.FileName, msg); |
| } |
| // Create mapping |
| if (!MocRegisterIncluded(incKey.Key, source, std::move(header), true)) { |
| return false; |
| } |
| } |
| } else { |
| // Strict mode |
| for (IncludeKeyT const& incKey : parseData.Include.Dot) { |
| // Check if this is the sources own .moc file |
| bool const ownMoc = (incKey.Base == sourceBase); |
| if (!ownMoc) { |
| // Don't allow <BASE>.moc include other than own in regular mode |
| std::string msg = "The file includes the moc file "; |
| msg += Quoted(incKey.Key); |
| msg += ",\nwhich seems to be the moc file from a different " |
| "source file.\nThis is not supported. Include "; |
| msg += Quoted(sourceBase + ".moc"); |
| msg += " to run moc on this source file."; |
| LogFileError(GenT::MOC, sourceFile.FileName, msg); |
| return false; |
| } |
| // Accept but issue a warning if moc isn't required |
| if (parseData.Macro.empty()) { |
| std::string msg = "The file includes the moc file "; |
| msg += Quoted(incKey.Key); |
| msg += ", but does not contain a "; |
| msg += MocConst().MacrosString(); |
| msg += " macro."; |
| Log().WarningFile(GenT::MOC, sourceFile.FileName, msg); |
| } |
| // Create mapping |
| if (!MocRegisterIncluded(incKey.Key, source, source, false)) { |
| return false; |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| cmQtAutoMocUic::SourceFileHandleT |
| cmQtAutoMocUic::JobEvaluateT::MocFindIncludedHeader( |
| std::string const& includerDir, std::string const& includeBase) const |
| { |
| // Search in vicinity of the source |
| { |
| SourceFileHandleT res = MocFindHeader(includerDir + includeBase); |
| if (res) { |
| return res; |
| } |
| } |
| // Search in include directories |
| for (std::string const& path : MocConst().IncludePaths) { |
| std::string testPath = path; |
| testPath += '/'; |
| testPath += includeBase; |
| SourceFileHandleT res = MocFindHeader(testPath); |
| if (res) { |
| return res; |
| } |
| } |
| // Return without success |
| return SourceFileHandleT(); |
| } |
| |
| cmQtAutoMocUic::SourceFileHandleT cmQtAutoMocUic::JobEvaluateT::MocFindHeader( |
| std::string const& basePath) const |
| { |
| std::string testPath; |
| testPath.reserve(basePath.size() + 8); |
| for (std::string const& ext : BaseConst().HeaderExtensions) { |
| testPath.clear(); |
| testPath += basePath; |
| testPath += '.'; |
| testPath += ext; |
| cmFileTime fileTime; |
| if (fileTime.Load(testPath)) { |
| // Compute real path of the file |
| testPath = cmSystemTools::GetRealPath(testPath); |
| // Return a known file if it exists already |
| { |
| auto it = BaseEval().Headers.find(testPath); |
| if (it != BaseEval().Headers.end()) { |
| return it->second; |
| } |
| } |
| // Created and return discovered file entry |
| SourceFileHandleT& res = MocEval().HeadersDiscovered[testPath]; |
| if (!res) { |
| res = std::make_shared<SourceFileT>(testPath); |
| res->FileTime = fileTime; |
| res->Moc = true; |
| } |
| return res; |
| } |
| } |
| // Return without success |
| return SourceFileHandleT(); |
| } |
| |
| std::string cmQtAutoMocUic::JobEvaluateT::MocMessageTestHeaders( |
| std::string const& fileBase) const |
| { |
| std::ostringstream res; |
| { |
| std::string exts = ".{"; |
| exts += cmJoin(BaseConst().HeaderExtensions, ","); |
| exts += '}'; |
| // Compose result string |
| res << " " << fileBase << exts << '\n'; |
| for (std::string const& path : MocConst().IncludePaths) { |
| res << " " << path << '/' << fileBase << exts << '\n'; |
| } |
| } |
| return res.str(); |
| } |
| |
| bool cmQtAutoMocUic::JobEvaluateT::MocRegisterIncluded( |
| std::string const& includeString, SourceFileHandleT includerFileHandle, |
| SourceFileHandleT sourceFileHandle, bool sourceIsHeader) const |
| { |
| // Check if this file is already included |
| MappingHandleT& handle = MocEval().Includes[includeString]; |
| if (handle) { |
| // Check if the output file would be generated from different source files |
| if (handle->SourceFile != sourceFileHandle) { |
| std::string msg = "The source files\n "; |
| msg += Quoted(includerFileHandle->FileName); |
| msg += '\n'; |
| for (auto const& item : handle->IncluderFiles) { |
| msg += " "; |
| msg += Quoted(item->FileName); |
| msg += '\n'; |
| } |
| msg += "contain the same include string "; |
| msg += Quoted(includeString); |
| msg += ", but\nthe moc file would be generated from different " |
| "source files\n "; |
| msg += Quoted(sourceFileHandle->FileName); |
| msg += " and\n "; |
| msg += Quoted(handle->SourceFile->FileName); |
| msg += ".\nConsider to\n" |
| " - not include the \"moc_<NAME>.cpp\" file\n" |
| " - add a directory prefix to a \"<NAME>.moc\" include " |
| "(e.g \"sub/<NAME>.moc\")\n" |
| " - rename the source file(s)\n"; |
| LogError(GenT::MOC, msg); |
| return false; |
| } |
| |
| // The same mapping already exists. Just add to the includers list. |
| handle->IncluderFiles.emplace_back(std::move(includerFileHandle)); |
| return true; |
| } |
| |
| // Create a new mapping |
| handle = std::make_shared<MappingT>(); |
| handle->IncludeString = includeString; |
| handle->IncluderFiles.emplace_back(std::move(includerFileHandle)); |
| handle->SourceFile = std::move(sourceFileHandle); |
| handle->OutputFile += Gen()->AbsoluteIncludePath(includeString); |
| |
| // Register mapping in sources/headers map |
| MocRegisterMapping(handle, sourceIsHeader); |
| return true; |
| } |
| |
| void cmQtAutoMocUic::JobEvaluateT::MocRegisterMapping( |
| MappingHandleT mappingHandle, bool sourceIsHeader) const |
| { |
| auto& regMap = |
| sourceIsHeader ? MocEval().HeaderMappings : MocEval().SourceMappings; |
| // Check if source file already gets mapped |
| auto& regHandle = regMap[mappingHandle->SourceFile->FileName]; |
| if (!regHandle) { |
| // Yet unknown mapping |
| regHandle = std::move(mappingHandle); |
| } else { |
| // Mappings with include string override those without |
| if (!mappingHandle->IncludeString.empty()) { |
| regHandle = std::move(mappingHandle); |
| } |
| } |
| } |
| |
| bool cmQtAutoMocUic::JobEvaluateT::UicEval(SourceFileMapT const& fileMap) |
| { |
| for (auto const& pair : fileMap) { |
| if (!UicEvalFile(pair.second)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool cmQtAutoMocUic::JobEvaluateT::UicEvalFile( |
| SourceFileHandleT sourceFileHandle) |
| { |
| SourceFileT const& sourceFile = *sourceFileHandle; |
| auto const& Include = sourceFile.ParseData->Uic.Include; |
| if (!sourceFile.Uic || Include.empty()) { |
| return true; |
| } |
| |
| std::string const sourceDir = SubDirPrefix(sourceFile.FileName); |
| for (IncludeKeyT const& incKey : Include) { |
| // Find .ui file name |
| SourceFileHandleT uiFileHandle = |
| UicFindIncludedUi(sourceFile.FileName, sourceDir, incKey); |
| if (!uiFileHandle || UicConst().skipped(uiFileHandle->FileName)) { |
| continue; |
| } |
| // Register mapping |
| if (!UicRegisterMapping(incKey.Key, std::move(uiFileHandle), |
| std::move(sourceFileHandle))) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| bool cmQtAutoMocUic::JobEvaluateT::UicRegisterMapping( |
| std::string const& includeString, SourceFileHandleT uiFileHandle, |
| SourceFileHandleT includerFileHandle) |
| { |
| auto& Includes = Gen()->UicEval().Includes; |
| auto it = Includes.find(includeString); |
| if (it != Includes.end()) { |
| MappingHandleT const& handle = it->second; |
| if (handle->SourceFile != uiFileHandle) { |
| // The output file already gets generated - from a different .ui file! |
| std::string msg = "The source files\n "; |
| msg += Quoted(includerFileHandle->FileName); |
| msg += '\n'; |
| for (auto const& item : handle->IncluderFiles) { |
| msg += " "; |
| msg += Quoted(item->FileName); |
| msg += '\n'; |
| } |
| msg += "contain the same include string "; |
| msg += Quoted(includeString); |
| msg += ", but\nthe uic file would be generated from different " |
| "user interface files\n "; |
| msg += Quoted(uiFileHandle->FileName); |
| msg += " and\n "; |
| msg += Quoted(handle->SourceFile->FileName); |
| msg += ".\nConsider to\n" |
| " - add a directory prefix to a \"ui_<NAME>.h\" include " |
| "(e.g \"sub/ui_<NAME>.h\")\n" |
| " - rename the <NAME>.ui file(s) and adjust the \"ui_<NAME>.h\" " |
| "include(s)\n"; |
| LogError(GenT::UIC, msg); |
| return false; |
| } |
| // Add includer file to existing mapping |
| handle->IncluderFiles.emplace_back(std::move(includerFileHandle)); |
| } else { |
| // New mapping handle |
| MappingHandleT handle = std::make_shared<MappingT>(); |
| handle->IncludeString = includeString; |
| handle->IncluderFiles.emplace_back(std::move(includerFileHandle)); |
| handle->SourceFile = std::move(uiFileHandle); |
| handle->OutputFile += Gen()->AbsoluteIncludePath(includeString); |
| // Register mapping |
| Includes.emplace(includeString, std::move(handle)); |
| } |
| return true; |
| } |
| |
| cmQtAutoMocUic::SourceFileHandleT |
| cmQtAutoMocUic::JobEvaluateT::UicFindIncludedUi( |
| std::string const& sourceFile, std::string const& sourceDir, |
| IncludeKeyT const& incKey) const |
| { |
| std::string searchFileName = incKey.Base; |
| searchFileName += ".ui"; |
| // Collect search paths list |
| std::vector<std::string> testFiles; |
| { |
| auto& searchPaths = UicConst().SearchPaths; |
| testFiles.reserve((searchPaths.size() + 1) * 2); |
| |
| // Vicinity of the source |
| testFiles.emplace_back(sourceDir + searchFileName); |
| if (!incKey.Dir.empty()) { |
| std::string path = sourceDir; |
| path += incKey.Dir; |
| path += searchFileName; |
| testFiles.emplace_back(path); |
| } |
| // AUTOUIC search paths |
| if (!searchPaths.empty()) { |
| for (std::string const& sPath : searchPaths) { |
| std::string path = sPath; |
| path += '/'; |
| path += searchFileName; |
| testFiles.emplace_back(std::move(path)); |
| } |
| if (!incKey.Dir.empty()) { |
| for (std::string const& sPath : searchPaths) { |
| std::string path = sPath; |
| path += '/'; |
| path += incKey.Dir; |
| path += searchFileName; |
| testFiles.emplace_back(std::move(path)); |
| } |
| } |
| } |
| } |
| |
| // Search for the .ui file! |
| for (std::string const& testFile : testFiles) { |
| cmFileTime fileTime; |
| if (fileTime.Load(testFile)) { |
| // .ui file found in files system! |
| std::string realPath = cmSystemTools::GetRealPath(testFile); |
| // Get or create .ui file handle |
| SourceFileHandleT& handle = Gen()->UicEval().UiFiles[realPath]; |
| if (!handle) { |
| // The file wasn't registered, yet |
| handle = std::make_shared<SourceFileT>(realPath); |
| handle->FileTime = fileTime; |
| } |
| return handle; |
| } |
| } |
| |
| // Log error |
| { |
| std::string msg = "The file includes the uic file "; |
| msg += Quoted(incKey.Key); |
| msg += ",\nbut the user interface file "; |
| msg += Quoted(searchFileName); |
| msg += "\ncould not be found in the following locations\n"; |
| for (std::string const& testFile : testFiles) { |
| msg += " "; |
| msg += Quoted(testFile); |
| msg += '\n'; |
| } |
| LogFileError(GenT::UIC, sourceFile, msg); |
| } |
| |
| return SourceFileHandleT(); |
| } |
| |
| void cmQtAutoMocUic::JobGenerateT::Process() |
| { |
| // Add moc compile jobs |
| if (MocConst().Enabled) { |
| for (auto const& pair : MocEval().HeaderMappings) { |
| // Register if this mapping is a candidate for mocs_compilation.cpp |
| bool const compFile = pair.second->IncludeString.empty(); |
| if (compFile) { |
| MocEval().CompFiles.emplace_back(pair.second->SourceFile->BuildPath); |
| } |
| if (!MocGenerate(pair.second, compFile)) { |
| return; |
| } |
| } |
| for (auto const& pair : MocEval().SourceMappings) { |
| if (!MocGenerate(pair.second, false)) { |
| return; |
| } |
| } |
| |
| // Add mocs compilations job on demand |
| Gen()->WorkerPool().EmplaceJob<JobMocsCompilationT>(); |
| } |
| |
| // Add uic compile jobs |
| if (UicConst().Enabled) { |
| for (auto const& pair : Gen()->UicEval().Includes) { |
| if (!UicGenerate(pair.second)) { |
| return; |
| } |
| } |
| } |
| |
| // Add finish job |
| Gen()->WorkerPool().EmplaceJob<JobFinishT>(); |
| } |
| |
| bool cmQtAutoMocUic::JobGenerateT::MocGenerate(MappingHandleT const& mapping, |
| bool compFile) const |
| { |
| std::unique_ptr<std::string> reason; |
| if (Log().Verbose()) { |
| reason = cm::make_unique<std::string>(); |
| } |
| if (MocUpdate(*mapping, reason.get())) { |
| // Create the parent directory |
| if (!MakeParentDirectory(mapping->OutputFile)) { |
| LogFileError(GenT::MOC, mapping->OutputFile, |
| "Could not create parent directory."); |
| return false; |
| } |
| // Add moc job |
| Gen()->WorkerPool().EmplaceJob<JobMocT>(mapping, std::move(reason)); |
| // Check if a moc job for a mocs_compilation.cpp entry was generated |
| if (compFile) { |
| MocEval().CompUpdated = true; |
| } |
| } |
| return true; |
| } |
| |
| bool cmQtAutoMocUic::JobGenerateT::MocUpdate(MappingT const& mapping, |
| std::string* reason) const |
| { |
| std::string const& sourceFile = mapping.SourceFile->FileName; |
| std::string const& outputFile = mapping.OutputFile; |
| |
| // Test if the output file exists |
| cmFileTime outputFileTime; |
| if (!outputFileTime.Load(outputFile)) { |
| if (reason != nullptr) { |
| *reason = "Generating "; |
| *reason += Quoted(outputFile); |
| *reason += ", because it doesn't exist, from "; |
| *reason += Quoted(sourceFile); |
| } |
| return true; |
| } |
| |
| // Test if any setting changed |
| if (MocConst().SettingsChanged) { |
| if (reason != nullptr) { |
| *reason = "Generating "; |
| *reason += Quoted(outputFile); |
| *reason += ", because the uic settings changed, from "; |
| *reason += Quoted(sourceFile); |
| } |
| return true; |
| } |
| |
| // Test if the source file is newer |
| if (outputFileTime.Older(mapping.SourceFile->FileTime)) { |
| if (reason != nullptr) { |
| *reason = "Generating "; |
| *reason += Quoted(outputFile); |
| *reason += ", because it's older than its source file, from "; |
| *reason += Quoted(sourceFile); |
| } |
| return true; |
| } |
| |
| // Test if the moc_predefs file is newer |
| if (!MocConst().PredefsFileAbs.empty()) { |
| if (outputFileTime.Older(MocEval().PredefsTime)) { |
| if (reason != nullptr) { |
| *reason = "Generating "; |
| *reason += Quoted(outputFile); |
| *reason += ", because it's older than "; |
| *reason += Quoted(MocConst().PredefsFileAbs); |
| *reason += ", from "; |
| *reason += Quoted(sourceFile); |
| } |
| return true; |
| } |
| } |
| |
| // Test if the moc executable is newer |
| if (outputFileTime.Older(MocConst().ExecutableTime)) { |
| if (reason != nullptr) { |
| *reason = "Generating "; |
| *reason += Quoted(outputFile); |
| *reason += ", because it's older than the moc executable, from "; |
| *reason += Quoted(sourceFile); |
| } |
| return true; |
| } |
| |
| // Test if a dependency file is newer |
| { |
| // Check dependency timestamps |
| std::string const sourceDir = SubDirPrefix(sourceFile); |
| for (std::string const& dep : mapping.SourceFile->ParseData->Moc.Depends) { |
| // Find dependency file |
| auto const depMatch = MocFindDependency(sourceDir, dep); |
| if (depMatch.first.empty()) { |
| Log().WarningFile(GenT::MOC, sourceFile, |
| "Could not find dependency file " + Quoted(dep)); |
| continue; |
| } |
| // Test if dependency file is older |
| if (outputFileTime.Older(depMatch.second)) { |
| if (reason != nullptr) { |
| *reason = "Generating "; |
| *reason += Quoted(outputFile); |
| *reason += ", because it's older than its dependency file "; |
| *reason += Quoted(depMatch.first); |
| *reason += ", from "; |
| *reason += Quoted(sourceFile); |
| } |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| std::pair<std::string, cmFileTime> |
| cmQtAutoMocUic::JobGenerateT::MocFindDependency( |
| std::string const& sourceDir, std::string const& includeString) const |
| { |
| typedef std::pair<std::string, cmFileTime> ResPair; |
| // Search in vicinity of the source |
| { |
| ResPair res{ sourceDir + includeString, {} }; |
| if (res.second.Load(res.first)) { |
| return res; |
| } |
| } |
| // Search in include directories |
| for (std::string const& includePath : MocConst().IncludePaths) { |
| ResPair res{ includePath, {} }; |
| res.first += '/'; |
| res.first += includeString; |
| if (res.second.Load(res.first)) { |
| return res; |
| } |
| } |
| // Return empty |
| return ResPair(); |
| } |
| |
| bool cmQtAutoMocUic::JobGenerateT::UicGenerate( |
| MappingHandleT const& mapping) const |
| { |
| std::unique_ptr<std::string> reason; |
| if (Log().Verbose()) { |
| reason = cm::make_unique<std::string>(); |
| } |
| if (UicUpdate(*mapping, reason.get())) { |
| // Create the parent directory |
| if (!MakeParentDirectory(mapping->OutputFile)) { |
| LogFileError(GenT::UIC, mapping->OutputFile, |
| "Could not create parent directory."); |
| return false; |
| } |
| // Add uic job |
| Gen()->WorkerPool().EmplaceJob<JobUicT>(mapping, std::move(reason)); |
| } |
| return true; |
| } |
| |
| bool cmQtAutoMocUic::JobGenerateT::UicUpdate(MappingT const& mapping, |
| std::string* reason) const |
| { |
| std::string const& sourceFile = mapping.SourceFile->FileName; |
| std::string const& outputFile = mapping.OutputFile; |
| |
| // Test if the build file exists |
| cmFileTime outputFileTime; |
| if (!outputFileTime.Load(outputFile)) { |
| if (reason != nullptr) { |
| *reason = "Generating "; |
| *reason += Quoted(outputFile); |
| *reason += ", because it doesn't exist, from "; |
| *reason += Quoted(sourceFile); |
| } |
| return true; |
| } |
| |
| // Test if the uic settings changed |
| if (UicConst().SettingsChanged) { |
| if (reason != nullptr) { |
| *reason = "Generating "; |
| *reason += Quoted(outputFile); |
| *reason += ", because the uic settings changed, from "; |
| *reason += Quoted(sourceFile); |
| } |
| return true; |
| } |
| |
| // Test if the source file is newer |
| if (outputFileTime.Older(mapping.SourceFile->FileTime)) { |
| if (reason != nullptr) { |
| *reason = "Generating "; |
| *reason += Quoted(outputFile); |
| *reason += " because it's older than the source file "; |
| *reason += Quoted(sourceFile); |
| } |
| return true; |
| } |
| |
| // Test if the uic executable is newer |
| if (outputFileTime.Older(UicConst().ExecutableTime)) { |
| if (reason != nullptr) { |
| *reason = "Generating "; |
| *reason += Quoted(outputFile); |
| *reason += ", because it's older than the uic executable, from "; |
| *reason += Quoted(sourceFile); |
| } |
| return true; |
| } |
| |
| return false; |
| } |
| |
| void cmQtAutoMocUic::JobMocT::Process() |
| { |
| std::string const& sourceFile = Mapping->SourceFile->FileName; |
| std::string const& outputFile = Mapping->OutputFile; |
| |
| // Compose moc command |
| std::vector<std::string> cmd; |
| cmd.push_back(MocConst().Executable); |
| // Add options |
| cmAppend(cmd, MocConst().AllOptions); |
| // Add predefs include |
| if (!MocConst().PredefsFileAbs.empty()) { |
| cmd.emplace_back("--include"); |
| cmd.push_back(MocConst().PredefsFileAbs); |
| } |
| cmd.emplace_back("-o"); |
| cmd.push_back(outputFile); |
| cmd.push_back(sourceFile); |
| |
| // Execute moc command |
| cmWorkerPool::ProcessResultT result; |
| if (RunProcess(GenT::MOC, result, cmd, Reason.get())) { |
| // Moc command success. Print moc output. |
| if (!result.StdOut.empty()) { |
| Log().Info(GenT::MOC, result.StdOut); |
| } |
| } else { |
| // Moc command failed |
| std::string msg = "The moc process failed to compile\n "; |
| msg += Quoted(sourceFile); |
| msg += "\ninto\n "; |
| msg += Quoted(outputFile); |
| if (Mapping->IncluderFiles.empty()) { |
| msg += ".\n"; |
| } else { |
| msg += "\nincluded by\n"; |
| for (auto const& item : Mapping->IncluderFiles) { |
| msg += " "; |
| msg += Quoted(item->FileName); |
| msg += '\n'; |
| } |
| } |
| msg += result.ErrorMessage; |
| LogCommandError(GenT::MOC, msg, cmd, result.StdOut); |
| } |
| } |
| |
| void cmQtAutoMocUic::JobUicT::Process() |
| { |
| std::string const& sourceFile = Mapping->SourceFile->FileName; |
| std::string const& outputFile = Mapping->OutputFile; |
| |
| // Compose uic command |
| std::vector<std::string> cmd; |
| cmd.push_back(UicConst().Executable); |
| { |
| std::vector<std::string> allOpts = UicConst().TargetOptions; |
| auto optionIt = UicConst().Options.find(sourceFile); |
| if (optionIt != UicConst().Options.end()) { |
| UicMergeOptions(allOpts, optionIt->second, |
| (BaseConst().QtVersionMajor == 5)); |
| } |
| cmAppend(cmd, allOpts); |
| } |
| cmd.emplace_back("-o"); |
| cmd.emplace_back(outputFile); |
| cmd.emplace_back(sourceFile); |
| |
| cmWorkerPool::ProcessResultT result; |
| if (RunProcess(GenT::UIC, result, cmd, Reason.get())) { |
| // Uic command success |
| // Print uic output |
| if (!result.StdOut.empty()) { |
| Log().Info(GenT::UIC, result.StdOut); |
| } |
| } else { |
| // Uic command failed |
| std::string msg = "The uic process failed to compile\n "; |
| msg += Quoted(sourceFile); |
| msg += "\ninto\n "; |
| msg += Quoted(outputFile); |
| msg += "\nincluded by\n"; |
| for (auto const& item : Mapping->IncluderFiles) { |
| msg += " "; |
| msg += Quoted(item->FileName); |
| msg += '\n'; |
| } |
| msg += result.ErrorMessage; |
| LogCommandError(GenT::UIC, msg, cmd, result.StdOut); |
| } |
| } |
| |
| void cmQtAutoMocUic::JobMocsCompilationT::Process() |
| { |
| // Compose mocs compilation file content |
| std::string content = |
| "// This file is autogenerated. Changes will be overwritten.\n"; |
| |
| if (MocEval().CompFiles.empty()) { |
| // Placeholder content |
| content += "// No files found that require moc or the moc files are " |
| "included\n"; |
| content += "enum some_compilers { need_more_than_nothing };\n"; |
| } else { |
| // Valid content |
| char const clampB = BaseConst().MultiConfig ? '<' : '"'; |
| char const clampE = BaseConst().MultiConfig ? '>' : '"'; |
| for (std::string const& mocfile : MocEval().CompFiles) { |
| content += "#include "; |
| content += clampB; |
| content += mocfile; |
| content += clampE; |
| content += '\n'; |
| } |
| } |
| |
| std::string const& compAbs = MocConst().CompFileAbs; |
| if (cmQtAutoGenerator::FileDiffers(compAbs, content)) { |
| // Actually write mocs compilation file |
| if (Log().Verbose()) { |
| Log().Info(GenT::MOC, "Generating MOC compilation " + compAbs); |
| } |
| if (!FileWrite(compAbs, content)) { |
| LogFileError(GenT::MOC, compAbs, |
| "mocs compilation file writing failed."); |
| } |
| } else if (MocEval().CompUpdated) { |
| // Only touch mocs compilation file |
| if (Log().Verbose()) { |
| Log().Info(GenT::MOC, "Touching mocs compilation " + compAbs); |
| } |
| if (!cmSystemTools::Touch(compAbs, false)) { |
| LogFileError(GenT::MOC, compAbs, |
| "mocs compilation file touching failed."); |
| } |
| } |
| } |
| |
| void cmQtAutoMocUic::JobFinishT::Process() |
| { |
| Gen()->AbortSuccess(); |
| } |
| |
| cmQtAutoMocUic::cmQtAutoMocUic() = default; |
| cmQtAutoMocUic::~cmQtAutoMocUic() = default; |
| |
| bool cmQtAutoMocUic::Init(cmMakefile* makefile) |
| { |
| // Utility lambdas |
| auto InfoGet = [makefile](const char* key) { |
| return makefile->GetSafeDefinition(key); |
| }; |
| auto InfoGetBool = [makefile](const char* key) { |
| return makefile->IsOn(key); |
| }; |
| auto InfoGetList = [makefile](const char* key) -> std::vector<std::string> { |
| std::vector<std::string> list; |
| cmSystemTools::ExpandListArgument(makefile->GetSafeDefinition(key), list); |
| return list; |
| }; |
| auto InfoGetLists = |
| [makefile](const char* key) -> std::vector<std::vector<std::string>> { |
| std::vector<std::vector<std::string>> lists; |
| { |
| std::string const value = makefile->GetSafeDefinition(key); |
| std::string::size_type pos = 0; |
| while (pos < value.size()) { |
| std::string::size_type next = value.find(ListSep, pos); |
| std::string::size_type length = |
| (next != std::string::npos) ? next - pos : value.size() - pos; |
| // Remove enclosing braces |
| if (length >= 2) { |
| std::string::const_iterator itBeg = value.begin() + (pos + 1); |
| std::string::const_iterator itEnd = itBeg + (length - 2); |
| { |
| std::string subValue(itBeg, itEnd); |
| std::vector<std::string> list; |
| cmSystemTools::ExpandListArgument(subValue, list); |
| lists.push_back(std::move(list)); |
| } |
| } |
| pos += length; |
| pos += ListSep.size(); |
| } |
| } |
| return lists; |
| }; |
| auto InfoGetConfig = [makefile, this](const char* key) -> std::string { |
| const char* valueConf = nullptr; |
| { |
| std::string keyConf = key; |
| keyConf += '_'; |
| keyConf += InfoConfig(); |
| valueConf = makefile->GetDefinition(keyConf); |
| } |
| if (valueConf == nullptr) { |
| return makefile->GetSafeDefinition(key); |
| } |
| return std::string(valueConf); |
| }; |
| auto InfoGetConfigList = |
| [&InfoGetConfig](const char* key) -> std::vector<std::string> { |
| std::vector<std::string> list; |
| cmSystemTools::ExpandListArgument(InfoGetConfig(key), list); |
| return list; |
| }; |
| auto LogInfoError = [this](std::string const& msg) -> bool { |
| std::ostringstream err; |
| err << "In " << Quoted(this->InfoFile()) << ":\n" << msg; |
| this->Log().Error(GenT::GEN, err.str()); |
| return false; |
| }; |
| auto MatchSizes = [&LogInfoError](const char* keyA, const char* keyB, |
| std::size_t sizeA, |
| std::size_t sizeB) -> bool { |
| if (sizeA == sizeB) { |
| return true; |
| } |
| std::ostringstream err; |
| err << "Lists sizes mismatch " << keyA << '(' << sizeA << ") " << keyB |
| << '(' << sizeB << ')'; |
| return LogInfoError(err.str()); |
| }; |
| |
| // -- Read info file |
| if (!makefile->ReadListFile(InfoFile())) { |
| return LogInfoError("File processing failed"); |
| } |
| |
| // -- Meta |
| Logger_.RaiseVerbosity(InfoGet("AM_VERBOSITY")); |
| BaseConst_.MultiConfig = InfoGetBool("AM_MULTI_CONFIG"); |
| { |
| unsigned long num = 1; |
| if (cmSystemTools::StringToULong(InfoGet("AM_PARALLEL").c_str(), &num)) { |
| num = std::max<unsigned long>(num, 1); |
| num = std::min<unsigned long>(num, ParallelMax); |
| } |
| WorkerPool_.SetThreadCount(static_cast<unsigned int>(num)); |
| } |
| BaseConst_.HeaderExtensions = |
| makefile->GetCMakeInstance()->GetHeaderExtensions(); |
| |
| // - Files and directories |
| BaseConst_.IncludeProjectDirsBefore = |
| InfoGetBool("AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE"); |
| BaseConst_.ProjectSourceDir = InfoGet("AM_CMAKE_SOURCE_DIR"); |
| BaseConst_.ProjectBinaryDir = InfoGet("AM_CMAKE_BINARY_DIR"); |
| BaseConst_.CurrentSourceDir = InfoGet("AM_CMAKE_CURRENT_SOURCE_DIR"); |
| BaseConst_.CurrentBinaryDir = InfoGet("AM_CMAKE_CURRENT_BINARY_DIR"); |
| BaseConst_.AutogenBuildDir = InfoGet("AM_BUILD_DIR"); |
| if (BaseConst_.AutogenBuildDir.empty()) { |
| return LogInfoError("Autogen build directory missing."); |
| } |
| BaseConst_.AutogenIncludeDir = InfoGetConfig("AM_INCLUDE_DIR"); |
| if (BaseConst_.AutogenIncludeDir.empty()) { |
| return LogInfoError("Autogen include directory missing."); |
| } |
| BaseConst_.CMakeExecutable = InfoGetConfig("AM_CMAKE_EXECUTABLE"); |
| if (BaseConst_.CMakeExecutable.empty()) { |
| return LogInfoError("CMake executable file name missing."); |
| } |
| if (!BaseConst_.CMakeExecutableTime.Load(BaseConst_.CMakeExecutable)) { |
| std::string error = "The CMake executable "; |
| error += Quoted(BaseConst_.CMakeExecutable); |
| error += " does not exist."; |
| return LogInfoError(error); |
| } |
| BaseConst_.ParseCacheFile = InfoGetConfig("AM_PARSE_CACHE_FILE"); |
| if (BaseConst_.ParseCacheFile.empty()) { |
| return LogInfoError("Parse cache file name missing."); |
| } |
| |
| // - Settings file |
| SettingsFile_ = InfoGetConfig("AM_SETTINGS_FILE"); |
| if (SettingsFile_.empty()) { |
| return LogInfoError("Settings file name missing."); |
| } |
| |
| // - Qt environment |
| { |
| unsigned long qtv = BaseConst_.QtVersionMajor; |
| if (cmSystemTools::StringToULong(InfoGet("AM_QT_VERSION_MAJOR").c_str(), |
| &qtv)) { |
| BaseConst_.QtVersionMajor = static_cast<unsigned int>(qtv); |
| } |
| } |
| |
| // - Moc |
| MocConst_.Executable = InfoGet("AM_QT_MOC_EXECUTABLE"); |
| if (!MocConst().Executable.empty()) { |
| MocConst_.Enabled = true; |
| // Load the executable file time |
| if (!MocConst_.ExecutableTime.Load(MocConst_.Executable)) { |
| std::string error = "The moc executable "; |
| error += Quoted(MocConst_.Executable); |
| error += " does not exist."; |
| return LogInfoError(error); |
| } |
| for (std::string& sfl : InfoGetList("AM_MOC_SKIP")) { |
| MocConst_.SkipList.insert(std::move(sfl)); |
| } |
| MocConst_.Definitions = InfoGetConfigList("AM_MOC_DEFINITIONS"); |
| MocConst_.IncludePaths = InfoGetConfigList("AM_MOC_INCLUDES"); |
| MocConst_.Options = InfoGetList("AM_MOC_OPTIONS"); |
| MocConst_.RelaxedMode = InfoGetBool("AM_MOC_RELAXED_MODE"); |
| for (std::string const& item : InfoGetList("AM_MOC_MACRO_NAMES")) { |
| MocConst_.MacroFilters.emplace_back( |
| item, ("[\n][ \t]*{?[ \t]*" + item).append("[^a-zA-Z0-9_]")); |
| } |
| { |
| auto addFilter = [this, &LogInfoError](std::string const& key, |
| std::string const& exp) -> bool { |
| auto filterErr = [&LogInfoError, &key, &exp](const char* err) -> bool { |
| std::ostringstream ferr; |
| ferr << "AUTOMOC_DEPEND_FILTERS: " << err << '\n'; |
| ferr << " Key: " << Quoted(key) << '\n'; |
| ferr << " Exp: " << Quoted(exp) << '\n'; |
| return LogInfoError(ferr.str()); |
| }; |
| if (key.empty()) { |
| return filterErr("Key is empty"); |
| } |
| if (exp.empty()) { |
| return filterErr("Regular expression is empty"); |
| } |
| this->MocConst_.DependFilters.emplace_back(key, exp); |
| if (!this->MocConst_.DependFilters.back().Exp.is_valid()) { |
| return filterErr("Regular expression compiling failed"); |
| } |
| return true; |
| }; |
| |
| // Insert default filter for Q_PLUGIN_METADATA |
| if (BaseConst().QtVersionMajor != 4) { |
| if (!addFilter("Q_PLUGIN_METADATA", |
| "[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\(" |
| "[^\\)]*FILE[ \t]*\"([^\"]+)\"")) { |
| return false; |
| } |
| } |
| // Insert user defined dependency filters |
| std::vector<std::string> flts = InfoGetList("AM_MOC_DEPEND_FILTERS"); |
| if ((flts.size() % 2) != 0) { |
| return LogInfoError( |
| "AUTOMOC_DEPEND_FILTERS list size is not a multiple of 2"); |
| } |
| for (auto itC = flts.begin(), itE = flts.end(); itC != itE; itC += 2) { |
| if (!addFilter(*itC, *(itC + 1))) { |
| return false; |
| } |
| } |
| } |
| MocConst_.PredefsCmd = InfoGetList("AM_MOC_PREDEFS_CMD"); |
| } |
| |
| // - Uic |
| UicConst_.Executable = InfoGet("AM_QT_UIC_EXECUTABLE"); |
| if (!UicConst().Executable.empty()) { |
| UicConst_.Enabled = true; |
| // Load the executable file time |
| if (!UicConst_.ExecutableTime.Load(UicConst_.Executable)) { |
| std::string error = "The uic executable "; |
| error += Quoted(UicConst_.Executable); |
| error += " does not exist."; |
| return LogInfoError(error); |
| } |
| for (std::string& sfl : InfoGetList("AM_UIC_SKIP")) { |
| UicConst_.SkipList.insert(std::move(sfl)); |
| } |
| UicConst_.SearchPaths = InfoGetList("AM_UIC_SEARCH_PATHS"); |
| UicConst_.TargetOptions = InfoGetConfigList("AM_UIC_TARGET_OPTIONS"); |
| { |
| const char* keyFiles = "AM_UIC_OPTIONS_FILES"; |
| const char* keyOpts = "AM_UIC_OPTIONS_OPTIONS"; |
| auto sources = InfoGetList(keyFiles); |
| auto options = InfoGetLists(keyOpts); |
| if (!MatchSizes(keyFiles, keyOpts, sources.size(), options.size())) { |
| return false; |
| } |
| auto fitEnd = sources.cend(); |
| auto fit = sources.begin(); |
| auto oit = options.begin(); |
| while (fit != fitEnd) { |
| UicConst_.Options[*fit] = std::move(*oit); |
| ++fit; |
| ++oit; |
| } |
| } |
| } |
| |
| // - Headers and sources |
| { |
| auto makeSource = |
| [&LogInfoError](std::string const& fileName, |
| std::string const& fileFlags) -> SourceFileHandleT { |
| if (fileFlags.size() != 2) { |
| LogInfoError("Invalid file flags string size"); |
| return SourceFileHandleT(); |
| } |
| cmFileTime fileTime; |
| if (!fileTime.Load(fileName)) { |
| LogInfoError("The source file " + cmQtAutoGen::Quoted(fileName) + |
| " does not exist."); |
| return SourceFileHandleT(); |
| } |
| SourceFileHandleT sfh = std::make_shared<SourceFileT>(fileName); |
| sfh->FileTime = fileTime; |
| sfh->Moc = (fileFlags[0] == 'M'); |
| sfh->Uic = (fileFlags[1] == 'U'); |
| return sfh; |
| }; |
| |
| // Headers |
| { |
| // Get file lists |
| const char *keyFiles = "AM_HEADERS", *keyFlags = "AM_HEADERS_FLAGS"; |
| std::vector<std::string> files = InfoGetList(keyFiles); |
| std::vector<std::string> flags = InfoGetList(keyFlags); |
| std::vector<std::string> builds; |
| if (!MatchSizes(keyFiles, keyFlags, files.size(), flags.size())) { |
| return false; |
| } |
| if (MocConst().Enabled) { |
| const char* keyPaths = "AM_HEADERS_BUILD_PATHS"; |
| builds = InfoGetList(keyPaths); |
| if (!MatchSizes(keyFiles, keyPaths, files.size(), builds.size())) { |
| return false; |
| } |
| } |
| // Process file lists |
| for (std::size_t ii = 0; ii != files.size(); ++ii) { |
| std::string& fileName(files[ii]); |
| SourceFileHandleT sfh = makeSource(fileName, flags[ii]); |
| if (!sfh) { |
| return false; |
| } |
| if (MocConst().Enabled) { |
| sfh->BuildPath = std::move(builds[ii]); |
| if (sfh->BuildPath.empty()) { |
| Log().ErrorFile(GenT::GEN, this->InfoFile(), |
| "Header file build path is empty"); |
| return false; |
| } |
| } |
| BaseEval().Headers.emplace(std::move(fileName), std::move(sfh)); |
| } |
| } |
| |
| // Sources |
| { |
| const char *keyFiles = "AM_SOURCES", *keyFlags = "AM_SOURCES_FLAGS"; |
| std::vector<std::string> files = InfoGetList(keyFiles); |
| std::vector<std::string> flags = InfoGetList(keyFlags); |
| if (!MatchSizes(keyFiles, keyFlags, files.size(), flags.size())) { |
| return false; |
| } |
| // Process file lists |
| for (std::size_t ii = 0; ii != files.size(); ++ii) { |
| std::string& fileName(files[ii]); |
| SourceFileHandleT sfh = makeSource(fileName, flags[ii]); |
| if (!sfh) { |
| return false; |
| } |
| BaseEval().Sources.emplace(std::move(fileName), std::move(sfh)); |
| } |
| } |
| } |
| |
| // Init derived information |
| // ------------------------ |
| |
| // Moc variables |
| if (MocConst().Enabled) { |
| // Mocs compilation file |
| MocConst_.CompFileAbs = AbsoluteBuildPath("mocs_compilation.cpp"); |
| |
| // Moc predefs file |
| if (!MocConst_.PredefsCmd.empty()) { |
| MocConst_.PredefsFileRel = "moc_predefs"; |
| if (BaseConst_.MultiConfig) { |
| MocConst_.PredefsFileRel += '_'; |
| MocConst_.PredefsFileRel += InfoConfig(); |
| } |
| MocConst_.PredefsFileRel += ".h"; |
| MocConst_.PredefsFileAbs = AbsoluteBuildPath(MocConst().PredefsFileRel); |
| } |
| |
| // Sort include directories on demand |
| if (BaseConst().IncludeProjectDirsBefore) { |
| // Move strings to temporary list |
| std::list<std::string> includes(MocConst().IncludePaths.begin(), |
| MocConst().IncludePaths.end()); |
| MocConst_.IncludePaths.clear(); |
| MocConst_.IncludePaths.reserve(includes.size()); |
| // Append project directories only |
| { |
| std::array<std::string const*, 2> const movePaths = { |
| { &BaseConst().ProjectBinaryDir, &BaseConst().ProjectSourceDir } |
| }; |
| for (std::string const* ppath : movePaths) { |
| std::list<std::string>::iterator it = includes.begin(); |
| while (it != includes.end()) { |
| std::string const& path = *it; |
| if (cmSystemTools::StringStartsWith(path, ppath->c_str())) { |
| MocConst_.IncludePaths.push_back(path); |
| it = includes.erase(it); |
| } else { |
| ++it; |
| } |
| } |
| } |
| } |
| // Append remaining directories |
| MocConst_.IncludePaths.insert(MocConst_.IncludePaths.end(), |
| includes.begin(), includes.end()); |
| } |
| // Compose moc includes list |
| { |
| std::set<std::string> frameworkPaths; |
| for (std::string const& path : MocConst().IncludePaths) { |
| MocConst_.Includes.push_back("-I" + path); |
| // Extract framework path |
| if (cmHasLiteralSuffix(path, ".framework/Headers")) { |
| // Go up twice to get to the framework root |
| std::vector<std::string> pathComponents; |
| cmSystemTools::SplitPath(path, pathComponents); |
| frameworkPaths.emplace(cmSystemTools::JoinPath( |
| pathComponents.begin(), pathComponents.end() - 2)); |
| } |
| } |
| // Append framework includes |
| for (std::string const& path : frameworkPaths) { |
| MocConst_.Includes.emplace_back("-F"); |
| MocConst_.Includes.push_back(path); |
| } |
| } |
| // Setup single list with all options |
| { |
| // Add includes |
| MocConst_.AllOptions.insert(MocConst_.AllOptions.end(), |
| MocConst().Includes.begin(), |
| MocConst().Includes.end()); |
| // Add definitions |
| for (std::string const& def : MocConst().Definitions) { |
| MocConst_.AllOptions.push_back("-D" + def); |
| } |
| // Add options |
| MocConst_.AllOptions.insert(MocConst_.AllOptions.end(), |
| MocConst().Options.begin(), |
| MocConst().Options.end()); |
| } |
| } |
| |
| return true; |
| } |
| |
| template <class JOBTYPE> |
| void cmQtAutoMocUic::CreateParseJobs(SourceFileMapT const& sourceMap) |
| { |
| cmFileTime const parseCacheTime = BaseEval().ParseCacheTime; |
| ParseCacheT& parseCache = BaseEval().ParseCache; |
| for (auto& src : sourceMap) { |
| // Get or create the file parse data reference |
| ParseCacheT::GetOrInsertT cacheEntry = parseCache.GetOrInsert(src.first); |
| src.second->ParseData = std::move(cacheEntry.first); |
| // Create a parse job if the cache file was missing or is older |
| if (cacheEntry.second || src.second->FileTime.Newer(parseCacheTime)) { |
| BaseEval().ParseCacheChanged = true; |
| WorkerPool().EmplaceJob<JOBTYPE>(src.second); |
| } |
| } |
| } |
| |
| void cmQtAutoMocUic::InitJobs() |
| { |
| // Add moc_predefs.h job |
| if (MocConst().Enabled && !MocConst().PredefsCmd.empty()) { |
| WorkerPool().EmplaceJob<JobMocPredefsT>(); |
| } |
| // Add header parse jobs |
| CreateParseJobs<JobParseHeaderT>(BaseEval().Headers); |
| // Add source parse jobs |
| CreateParseJobs<JobParseSourceT>(BaseEval().Sources); |
| // Add evaluate job |
| WorkerPool().EmplaceJob<JobEvaluateT>(); |
| } |
| |
| bool cmQtAutoMocUic::Process() |
| { |
| SettingsFileRead(); |
| ParseCacheRead(); |
| if (!CreateDirectories()) { |
| return false; |
| } |
| InitJobs(); |
| if (!WorkerPool_.Process(this)) { |
| return false; |
| } |
| if (JobError_) { |
| return false; |
| } |
| if (!ParseCacheWrite()) { |
| return false; |
| } |
| if (!SettingsFileWrite()) { |
| return false; |
| } |
| return true; |
| } |
| |
| void cmQtAutoMocUic::SettingsFileRead() |
| { |
| // Compose current settings strings |
| { |
| cmCryptoHash cryptoHash(cmCryptoHash::AlgoSHA256); |
| std::string const sep(";"); |
| auto cha = [&cryptoHash, &sep](std::string const& value) { |
| cryptoHash.Append(value); |
| cryptoHash.Append(sep); |
| }; |
| |
| if (MocConst_.Enabled) { |
| cryptoHash.Initialize(); |
| cha(MocConst().Executable); |
| for (auto const& value : MocConst().AllOptions) { |
| cha(value); |
| } |
| cha(BaseConst().IncludeProjectDirsBefore ? "TRUE" : "FALSE"); |
| for (auto const& value : MocConst().PredefsCmd) { |
| cha(value); |
| } |
| for (auto const& filter : MocConst().DependFilters) { |
| cha(filter.Key); |
| } |
| for (auto const& filter : MocConst().MacroFilters) { |
| cha(filter.Key); |
| } |
| SettingsStringMoc_ = cryptoHash.FinalizeHex(); |
| } |
| |
| if (UicConst().Enabled) { |
| cryptoHash.Initialize(); |
| cha(UicConst().Executable); |
| for (auto const& value : UicConst().TargetOptions) { |
| cha(value); |
| } |
| for (const auto& item : UicConst().Options) { |
| cha(item.first); |
| for (auto const& svalue : item.second) { |
| cha(svalue); |
| } |
| } |
| SettingsStringUic_ = cryptoHash.FinalizeHex(); |
| } |
| } |
| |
| // Read old settings and compare |
| { |
| std::string content; |
| if (cmQtAutoGenerator::FileRead(content, SettingsFile_)) { |
| if (MocConst().Enabled) { |
| if (SettingsStringMoc_ != SettingsFind(content, "moc")) { |
| MocConst_.SettingsChanged = true; |
| } |
| } |
| if (UicConst().Enabled) { |
| if (SettingsStringUic_ != SettingsFind(content, "uic")) { |
| UicConst_.SettingsChanged = true; |
| } |
| } |
| // In case any setting changed remove the old settings file. |
| // This triggers a full rebuild on the next run if the current |
| // build is aborted before writing the current settings in the end. |
| if (MocConst().SettingsChanged || UicConst().SettingsChanged) { |
| cmSystemTools::RemoveFile(SettingsFile_); |
| } |
| } else { |
| // Settings file read failed |
| if (MocConst().Enabled) { |
| MocConst_.SettingsChanged = true; |
| } |
| if (UicConst().Enabled) { |
| UicConst_.SettingsChanged = true; |
| } |
| } |
| } |
| } |
| |
| bool cmQtAutoMocUic::SettingsFileWrite() |
| { |
| // Only write if any setting changed |
| if (MocConst().SettingsChanged || UicConst().SettingsChanged) { |
| if (Log().Verbose()) { |
| Log().Info(GenT::GEN, "Writing settings file " + Quoted(SettingsFile_)); |
| } |
| // Compose settings file content |
| std::string content; |
| { |
| auto SettingAppend = [&content](const char* key, |
| std::string const& value) { |
| if (!value.empty()) { |
| content += key; |
| content += ':'; |
| content += value; |
| content += '\n'; |
| } |
| }; |
| SettingAppend("moc", SettingsStringMoc_); |
| SettingAppend("uic", SettingsStringUic_); |
| } |
| // Write settings file |
| std::string error; |
| if (!cmQtAutoGenerator::FileWrite(SettingsFile_, content, &error)) { |
| Log().ErrorFile(GenT::GEN, SettingsFile_, |
| "Settings file writing failed. " + error); |
| // Remove old settings file to trigger a full rebuild on the next run |
| cmSystemTools::RemoveFile(SettingsFile_); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| void cmQtAutoMocUic::ParseCacheRead() |
| { |
| const char* reason = nullptr; |
| // Don't read the cache if it is invalid |
| if (!BaseEval().ParseCacheTime.Load(BaseConst().ParseCacheFile)) { |
| reason = "Refreshing parse cache because it doesn't exist."; |
| } else if (MocConst().SettingsChanged || UicConst().SettingsChanged) { |
| reason = "Refreshing parse cache because the settings changed."; |
| } else if (BaseEval().ParseCacheTime.Older( |
| BaseConst().CMakeExecutableTime)) { |
| reason = |
| "Refreshing parse cache because it is older than the CMake executable."; |
| } |
| |
| if (reason != nullptr) { |
| // Don't read but refresh the complete parse cache |
| if (Log().Verbose()) { |
| Log().Info(GenT::GEN, reason); |
| } |
| BaseEval().ParseCacheChanged = true; |
| } else { |
| // Read parse cache |
| BaseEval().ParseCache.ReadFromFile(BaseConst().ParseCacheFile); |
| } |
| } |
| |
| bool cmQtAutoMocUic::ParseCacheWrite() |
| { |
| if (BaseEval().ParseCacheChanged) { |
| if (Log().Verbose()) { |
| Log().Info(GenT::GEN, |
| "Writing parse cache file " + |
| Quoted(BaseConst().ParseCacheFile)); |
| } |
| if (!BaseEval().ParseCache.WriteToFile(BaseConst().ParseCacheFile)) { |
| Log().ErrorFile(GenT::GEN, BaseConst().ParseCacheFile, |
| "Parse cache file writing failed."); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool cmQtAutoMocUic::CreateDirectories() |
| { |
| // Create AUTOGEN include directory |
| if (!cmSystemTools::MakeDirectory(BaseConst().AutogenIncludeDir)) { |
| Log().ErrorFile(GenT::GEN, BaseConst().AutogenIncludeDir, |
| "Could not create directory."); |
| return false; |
| } |
| return true; |
| } |
| |
| void cmQtAutoMocUic::Abort(bool error) |
| { |
| if (error) { |
| JobError_.store(true); |
| } |
| WorkerPool_.Abort(); |
| } |
| |
| std::string cmQtAutoMocUic::AbsoluteBuildPath( |
| std::string const& relativePath) const |
| { |
| std::string res(BaseConst().AutogenBuildDir); |
| res += '/'; |
| res += relativePath; |
| return res; |
| } |
| |
| std::string cmQtAutoMocUic::AbsoluteIncludePath( |
| std::string const& relativePath) const |
| { |
| std::string res(BaseConst().AutogenIncludeDir); |
| res += '/'; |
| res += relativePath; |
| return res; |
| } |