diff options
author | Sebastian Holtermann <sebholt@xwmw.org> | 2017-02-17 10:56:02 (GMT) |
---|---|---|
committer | Brad King <brad.king@kitware.com> | 2017-02-21 15:12:54 (GMT) |
commit | 03df033bfa551e83c2a265cc22f6f5278c80528b (patch) | |
tree | 2909a34b26b5a87fe06ff802e748b75f76d351a9 /Source/cmQtAutoGenerators.cxx | |
parent | 3ec230de1f45f48a8bbb8eca49674f0e0c99dfa1 (diff) | |
download | CMake-03df033bfa551e83c2a265cc22f6f5278c80528b.zip CMake-03df033bfa551e83c2a265cc22f6f5278c80528b.tar.gz CMake-03df033bfa551e83c2a265cc22f6f5278c80528b.tar.bz2 |
Autogen: Rebuild moc when Q_PLUGIN_METADATA json file changes
Closes #15419
Diffstat (limited to 'Source/cmQtAutoGenerators.cxx')
-rw-r--r-- | Source/cmQtAutoGenerators.cxx | 230 |
1 files changed, 166 insertions, 64 deletions
diff --git a/Source/cmQtAutoGenerators.cxx b/Source/cmQtAutoGenerators.cxx index 25a0f87..3965503 100644 --- a/Source/cmQtAutoGenerators.cxx +++ b/Source/cmQtAutoGenerators.cxx @@ -565,6 +565,16 @@ void cmQtAutoGenerators::Init(cmMakefile* makefile) this->MocIncludes.push_back(*it); } } + + // Insert MocDependFilter for Q_PLUGIN_METADATA + if (QtMajorVersion != "4") { + MocDependFilter filter; + filter.key = "Q_PLUGIN_METADATA"; + filter.regExp.compile("[\n][ \t]*" + "Q_PLUGIN_METADATA[ \t]*\\(" + "[^\\)]*FILE[ \t]*\"([^\"]+)\""); + this->MocDependFilters.push_back(filter); + } } bool cmQtAutoGenerators::RunAutogen() @@ -580,6 +590,7 @@ bool cmQtAutoGenerators::RunAutogen() // key = moc source filepath, value = moc output filepath std::map<std::string, std::string> mocsIncluded; std::map<std::string, std::string> mocsNotIncluded; + std::map<std::string, std::set<std::string> > mocDepends; std::map<std::string, std::vector<std::string> > uisIncluded; // collects all headers which may need to be mocced std::set<std::string> mocHeaderFiles; @@ -590,8 +601,8 @@ bool cmQtAutoGenerators::RunAutogen() it != this->Sources.end(); ++it) { const std::string& absFilename = cmsys::SystemTools::GetRealPath(*it); // Parse source file for MOC/UIC - if (!this->ParseSourceFile(absFilename, mocsIncluded, uisIncluded, - this->MocRelaxedMode)) { + if (!this->ParseSourceFile(absFilename, mocsIncluded, mocDepends, + uisIncluded, this->MocRelaxedMode)) { return false; } // Find additional headers @@ -611,10 +622,10 @@ bool cmQtAutoGenerators::RunAutogen() } } this->ParseHeaders(mocHeaderFiles, uicHeaderFiles, mocsIncluded, - mocsNotIncluded, uisIncluded); + mocsNotIncluded, mocDepends, uisIncluded); // Generate files - if (!this->MocGenerateAll(mocsIncluded, mocsNotIncluded)) { + if (!this->MocGenerateAll(mocsIncluded, mocsNotIncluded, mocDepends)) { return false; } if (!this->UicGenerateAll(uisIncluded)) { @@ -631,17 +642,18 @@ bool cmQtAutoGenerators::RunAutogen() * @brief Tests if the C++ content requires moc processing * @return True if moc is required */ -bool cmQtAutoGenerators::MocRequired(const std::string& text, +bool cmQtAutoGenerators::MocRequired(const std::string& contentText, std::string* macroName) { for (unsigned int ii = 0; ii != cmArraySize(this->MacroFilters); ++ii) { - MacroFilter& macroFilter = this->MacroFilters[ii]; - // Run a simple check before an expensive regular expression check - if (text.find(macroFilter.first) != std::string::npos) { - if (macroFilter.second.find(text)) { + MacroFilter& filter = this->MacroFilters[ii]; + // Run a simple find string operation before the expensive + // regular expression check + if (contentText.find(filter.first) != std::string::npos) { + if (filter.second.find(contentText)) { // Return macro name on demand if (macroName != CM_NULLPTR) { - *macroName = macroFilter.first; + *macroName = filter.first; } return true; } @@ -650,6 +662,44 @@ bool cmQtAutoGenerators::MocRequired(const std::string& text, return false; } +void cmQtAutoGenerators::MocFindDepends( + const std::string& absFilename, const std::string& contentText, + std::map<std::string, std::set<std::string> >& mocDepends) +{ + for (std::vector<MocDependFilter>::iterator fit = + this->MocDependFilters.begin(); + fit != this->MocDependFilters.end(); ++fit) { + MocDependFilter& filter = *fit; + // Run a simple find string operation before the expensive + // regular expression check + if (contentText.find(filter.key) != std::string::npos) { + // Run regular expression check loop + const char* contentChars = contentText.c_str(); + while (filter.regExp.find(contentChars)) { + // Evaluate match + const std::string match = filter.regExp.match(1); + if (!match.empty()) { + // Find the dependency file + const std::string incFile = + this->FindIncludedFile(absFilename, match); + if (!incFile.empty()) { + mocDepends[absFilename].insert(incFile); + if (this->Verbose) { + this->LogInfo("AutoMoc: Found dependency:\n \"" + absFilename + + "\"\n \"" + incFile + "\"\n"); + } + } else { + this->LogWarning("AutoMoc: Warning: \"" + absFilename + "\"\n" + + "Could not find dependency file \"" + match + + "\"\n"); + } + } + contentChars += filter.regExp.end(); + } + } + } +} + /** * @brief Tests if the file should be ignored for moc scanning * @return True if the file should be ignored @@ -685,11 +735,12 @@ bool cmQtAutoGenerators::UicSkip(const std::string& absFilename) const bool cmQtAutoGenerators::ParseSourceFile( const std::string& absFilename, std::map<std::string, std::string>& mocsIncluded, + std::map<std::string, std::set<std::string> >& mocDepends, std::map<std::string, std::vector<std::string> >& uisIncluded, bool relaxed) { bool success = true; - const std::string contentsString = ReadAll(absFilename); - if (contentsString.empty()) { + const std::string contentText = ReadAll(absFilename); + if (contentText.empty()) { std::ostringstream err; err << "AutoGen: Warning: " << absFilename << "\n" << "The file is empty\n"; @@ -697,19 +748,19 @@ bool cmQtAutoGenerators::ParseSourceFile( } else { // Parse source contents for MOC if (success && !this->MocSkip(absFilename)) { - success = this->ParseContentForMoc(absFilename, contentsString, - mocsIncluded, relaxed); + success = this->MocParseSourceContent(absFilename, contentText, + mocsIncluded, mocDepends, relaxed); } // Parse source contents for UIC if (success && !this->UicSkip(absFilename)) { - this->ParseContentForUic(absFilename, contentsString, uisIncluded); + this->UicParseContent(absFilename, contentText, uisIncluded); } } return success; } -void cmQtAutoGenerators::ParseContentForUic( - const std::string& absFilename, const std::string& contentsString, +void cmQtAutoGenerators::UicParseContent( + const std::string& absFilename, const std::string& contentText, std::map<std::string, std::vector<std::string> >& uisIncluded) { if (this->Verbose) { @@ -718,7 +769,7 @@ void cmQtAutoGenerators::ParseContentForUic( this->LogInfo(err.str()); } - const char* contentChars = contentsString.c_str(); + const char* contentChars = contentText.c_str(); if (strstr(contentChars, "ui_") != CM_NULLPTR) { while (this->RegExpUicInclude.find(contentChars)) { const std::string currentUi = this->RegExpUicInclude.match(1); @@ -735,9 +786,10 @@ void cmQtAutoGenerators::ParseContentForUic( /** * @return True on success */ -bool cmQtAutoGenerators::ParseContentForMoc( - const std::string& absFilename, const std::string& contentsString, - std::map<std::string, std::string>& mocsIncluded, bool relaxed) +bool cmQtAutoGenerators::MocParseSourceContent( + const std::string& absFilename, const std::string& contentText, + std::map<std::string, std::string>& mocsIncluded, + std::map<std::string, std::set<std::string> >& mocDepends, bool relaxed) { if (this->Verbose) { std::ostringstream err; @@ -751,16 +803,15 @@ bool cmQtAutoGenerators::ParseContentForMoc( cmsys::SystemTools::GetFilenameWithoutLastExtension(absFilename); std::string macroName; - const bool requiresMoc = this->MocRequired(contentsString, ¯oName); + const bool requiresMoc = this->MocRequired(contentText, ¯oName); bool ownDotMocIncluded = false; - bool ownMocUnderscoreIncluded = false; - std::string ownMocUnderscoreFile; - std::string ownMocHeaderFile; + std::string ownMocUnderscoreInclude; + std::string ownMocUnderscoreHeader; // first a simple string check for "moc" is *much* faster than the regexp, // and if the string search already fails, we don't have to try the // expensive regexp - const char* contentChars = contentsString.c_str(); + const char* contentChars = contentText.c_str(); if (strstr(contentChars, "moc") != CM_NULLPTR) { // Iterate over all included moc files while (this->RegExpMocInclude.find(contentChars)) { @@ -786,11 +837,13 @@ bool cmQtAutoGenerators::ParseContentForMoc( const std::string headerToMoc = this->FindMocHeader(scannedFileAbsPath, incRealBasename, incSubDir); if (!headerToMoc.empty()) { + // Register moc job mocsIncluded[headerToMoc] = incString; + this->MocFindDepends(headerToMoc, contentText, mocDepends); + // Store meta information for relaxed mode if (relaxed && (incRealBasename == scannedFileBasename)) { - ownMocUnderscoreIncluded = true; - ownMocUnderscoreFile = incString; - ownMocHeaderFile = headerToMoc; + ownMocUnderscoreInclude = incString; + ownMocUnderscoreHeader = headerToMoc; } } else { std::ostringstream err; @@ -879,6 +932,7 @@ bool cmQtAutoGenerators::ParseContentForMoc( } if (!fileToMoc.empty()) { mocsIncluded[fileToMoc] = incString; + this->MocFindDepends(fileToMoc, contentText, mocDepends); } } // Forward content pointer @@ -891,14 +945,14 @@ bool cmQtAutoGenerators::ParseContentForMoc( // If this is the case, the moc_foo.cpp should probably be generated from // foo.cpp instead of foo.h, because otherwise it won't build. // But warn, since this is not how it is supposed to be used. - if (relaxed && ownMocUnderscoreIncluded) { + if (relaxed && !ownMocUnderscoreInclude.empty()) { // This is for KDE4 compatibility: std::ostringstream err; err << "AutoMoc: Warning: " << absFilename << "\n" << "The file contains a " << macroName << " macro, but does not include " << "\"" << scannedFileBasename << ".moc\", but instead includes " - << "\"" << ownMocUnderscoreFile << "\".\n" + << "\"" << ownMocUnderscoreInclude << "\".\n" << "Running moc on \"" << absFilename << "\"!\n" << "Better include \"" << scannedFileBasename << ".moc\" for compatibility with " @@ -906,8 +960,10 @@ bool cmQtAutoGenerators::ParseContentForMoc( this->LogWarning(err.str()); // Use scanned source file instead of scanned header file as moc source - mocsIncluded[absFilename] = ownMocUnderscoreFile; - mocsIncluded.erase(ownMocHeaderFile); + mocsIncluded[absFilename] = ownMocUnderscoreInclude; + this->MocFindDepends(absFilename, contentText, mocDepends); + // Remove + mocsIncluded.erase(ownMocUnderscoreHeader); } else { // Otherwise always error out since it will not compile: std::ostringstream err; @@ -923,6 +979,25 @@ bool cmQtAutoGenerators::ParseContentForMoc( return true; } +void cmQtAutoGenerators::MocParseHeaderContent( + const std::string& absFilename, const std::string& contentText, + std::map<std::string, std::string>& mocsNotIncluded, + std::map<std::string, std::set<std::string> >& mocDepends) +{ + // Log + if (this->Verbose) { + std::ostringstream err; + err << "AutoMoc: Checking " << absFilename << "\n"; + this->LogInfo(err.str()); + } + if (this->MocRequired(contentText)) { + // Register moc job + mocsNotIncluded[absFilename] = + this->ChecksumedPath(absFilename, "moc_", ".cpp"); + this->MocFindDepends(absFilename, contentText, mocDepends); + } +} + void cmQtAutoGenerators::SearchHeadersForSourceFile( const std::string& absFilename, std::set<std::string>& mocHeaderFiles, std::set<std::string>& uicHeaderFiles) const @@ -959,6 +1034,7 @@ void cmQtAutoGenerators::ParseHeaders( const std::set<std::string>& uicHeaderFiles, const std::map<std::string, std::string>& mocsIncluded, std::map<std::string, std::string>& mocsNotIncluded, + std::map<std::string, std::set<std::string> >& mocDepends, std::map<std::string, std::vector<std::string> >& uisIncluded) { // Merged header files list to read files only once @@ -969,33 +1045,26 @@ void cmQtAutoGenerators::ParseHeaders( for (std::set<std::string>::const_iterator hIt = headerFiles.begin(); hIt != headerFiles.end(); ++hIt) { const std::string& headerName = *hIt; - const std::string contents = ReadAll(headerName); + const std::string contentText = ReadAll(headerName); // Parse header content for MOC if ((mocHeaderFiles.find(headerName) != mocHeaderFiles.end()) && (mocsIncluded.find(headerName) == mocsIncluded.end())) { - // Log - if (this->Verbose) { - std::ostringstream err; - err << "AutoMoc: Checking " << headerName << "\n"; - this->LogInfo(err.str()); - } - if (this->MocRequired(contents)) { - mocsNotIncluded[headerName] = - this->ChecksumedPath(headerName, "moc_", ".cpp"); - } + this->MocParseHeaderContent(headerName, contentText, mocsNotIncluded, + mocDepends); } // Parse header content for UIC if (uicHeaderFiles.find(headerName) != uicHeaderFiles.end()) { - this->ParseContentForUic(headerName, contents, uisIncluded); + this->UicParseContent(headerName, contentText, uisIncluded); } } } bool cmQtAutoGenerators::MocGenerateAll( const std::map<std::string, std::string>& mocsIncluded, - const std::map<std::string, std::string>& mocsNotIncluded) + const std::map<std::string, std::string>& mocsNotIncluded, + const std::map<std::string, std::set<std::string> >& mocDepends) { if (!this->MocEnabled()) { return true; @@ -1023,11 +1092,11 @@ bool cmQtAutoGenerators::MocGenerateAll( // generate moc files that are included by source files. { - const std::string subDirPrefix = "include/"; + const std::string subDir = "include/"; for (std::map<std::string, std::string>::const_iterator it = mocsIncluded.begin(); it != mocsIncluded.end(); ++it) { - if (!this->MocGenerateFile(it->first, it->second, subDirPrefix)) { + if (!this->MocGenerateFile(it->first, it->second, subDir, mocDepends)) { if (this->RunMocFailed) { return false; } @@ -1038,11 +1107,11 @@ bool cmQtAutoGenerators::MocGenerateAll( // generate moc files that are _not_ included by source files. bool automocCppChanged = false; { - const std::string subDirPrefix; + const std::string subDir; for (std::map<std::string, std::string>::const_iterator it = mocsNotIncluded.begin(); it != mocsNotIncluded.end(); ++it) { - if (this->MocGenerateFile(it->first, it->second, subDirPrefix)) { + if (this->MocGenerateFile(it->first, it->second, subDir, mocDepends)) { automocCppChanged = true; } else { if (this->RunMocFailed) { @@ -1118,9 +1187,10 @@ bool cmQtAutoGenerators::MocGenerateAll( /** * @return True if a moc file was created. False may indicate an error. */ -bool cmQtAutoGenerators::MocGenerateFile(const std::string& sourceFile, - const std::string& mocFileName, - const std::string& subDirPrefix) +bool cmQtAutoGenerators::MocGenerateFile( + const std::string& sourceFile, const std::string& mocFileName, + const std::string& subDirPrefix, + const std::map<std::string, std::set<std::string> >& mocDepends) { bool mocGenerated = false; bool generateMoc = this->GenerateAllMoc; @@ -1132,6 +1202,20 @@ bool cmQtAutoGenerators::MocGenerateFile(const std::string& sourceFile, if (!generateMoc) { // Test if the source file is newer that the build file generateMoc = FileAbsentOrOlder(mocFileAbs, sourceFile); + if (!generateMoc) { + // Test if a dependency file changed + std::map<std::string, std::set<std::string> >::const_iterator dit = + mocDepends.find(sourceFile); + if (dit != mocDepends.end()) { + for (std::set<std::string>::const_iterator fit = dit->second.begin(); + fit != dit->second.end(); ++fit) { + if (FileAbsentOrOlder(mocFileAbs, *fit)) { + generateMoc = true; + break; + } + } + } + } } if (generateMoc) { // Log @@ -1489,33 +1573,34 @@ void cmQtAutoGenerators::LogErrorNameCollision( this->LogError(err.str()); } -void cmQtAutoGenerators::LogBold(const std::string& message) +void cmQtAutoGenerators::LogBold(const std::string& message) const { cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundBlue | cmsysTerminal_Color_ForegroundBold, message.c_str(), true, this->ColorOutput); } -void cmQtAutoGenerators::LogInfo(const std::string& message) +void cmQtAutoGenerators::LogInfo(const std::string& message) const { cmSystemTools::Stdout(message.c_str(), message.size()); } -void cmQtAutoGenerators::LogWarning(const std::string& message) +void cmQtAutoGenerators::LogWarning(const std::string& message) const { std::string msg(message); msg += "\n"; cmSystemTools::Stdout(msg.c_str(), msg.size()); } -void cmQtAutoGenerators::LogError(const std::string& message) +void cmQtAutoGenerators::LogError(const std::string& message) const { std::string msg(message); msg += "\n"; cmSystemTools::Stderr(msg.c_str(), msg.size()); } -void cmQtAutoGenerators::LogCommand(const std::vector<std::string>& command) +void cmQtAutoGenerators::LogCommand( + const std::vector<std::string>& command) const { std::ostringstream sbuf; for (std::vector<std::string>::const_iterator cmdIt = command.begin(); @@ -1639,23 +1724,40 @@ std::string cmQtAutoGenerators::FindMocHeader(const std::string& basePath, return header; } +std::string cmQtAutoGenerators::FindIncludedFile( + const std::string& sourceFile, const std::string& includeString) const +{ + // Search in vicinity of the source + { + std::string testPath = cmSystemTools::GetFilenamePath(sourceFile); + testPath += '/'; + testPath += includeString; + if (cmsys::SystemTools::FileExists(testPath.c_str())) { + return cmsys::SystemTools::GetRealPath(testPath); + } + } + // Search globaly + return FindInIncludeDirectories(includeString); +} + /** * @brief Tries to find a file in the include directories * @return True on success */ -bool cmQtAutoGenerators::FindInIncludeDirectories( - std::string& file_n, const std::string& searchString) const +std::string cmQtAutoGenerators::FindInIncludeDirectories( + const std::string& includeString) const { + std::string res; for (std::vector<std::string>::const_iterator iit = this->MocIncludePaths.begin(); iit != this->MocIncludePaths.end(); ++iit) { - const std::string fullPath = ((*iit) + '/' + searchString); + const std::string fullPath = ((*iit) + '/' + includeString); if (cmsys::SystemTools::FileExists(fullPath.c_str())) { - file_n = fullPath; - return true; + res = cmsys::SystemTools::GetRealPath(fullPath); + break; } } - return false; + return res; } /** |