diff options
-rw-r--r-- | Source/cmQtAutoGenerators.cxx | 208 | ||||
-rw-r--r-- | Source/cmQtAutoGenerators.h | 6 |
2 files changed, 214 insertions, 0 deletions
diff --git a/Source/cmQtAutoGenerators.cxx b/Source/cmQtAutoGenerators.cxx index a0949d4..41bb6f9 100644 --- a/Source/cmQtAutoGenerators.cxx +++ b/Source/cmQtAutoGenerators.cxx @@ -901,6 +901,214 @@ bool cmQtAutoGenerators::StrictParseCppFile( return true; } +/** + * @return True on success + */ +bool cmQtAutoGenerators::ParseSourceFile( + const std::string& absFilename, + const std::vector<std::string>& headerExtensions, + std::map<std::string, std::string>& includedMocs, + std::map<std::string, std::vector<std::string> >& includedUis, bool relaxed) +{ + cmsys::RegularExpression mocIncludeRegExp( + "[\n][ \t]*#[ \t]*include[ \t]+" + "[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]"); + + const std::string contentsString = ReadAll(absFilename); + if (contentsString.empty()) { + std::ostringstream err; + err << "AUTOGEN: warning: " << absFilename << "\n" + << "The file is empty\n"; + this->LogWarning(err.str()); + return true; + } + + // Parse source contents for UIC + this->ParseForUic(absFilename, contentsString, includedUis); + + // Continue with moc parsing on demand + if (this->MocExecutable.empty()) { + return true; + } + + const std::string scannedFileAbsPath = + cmsys::SystemTools::GetFilenamePath( + cmsys::SystemTools::GetRealPath(absFilename)) + + '/'; + const std::string scannedFileBasename = + cmsys::SystemTools::GetFilenameWithoutLastExtension(absFilename); + + std::string macroName; + const bool requiresMoc = requiresMocing(contentsString, macroName); + bool ownDotMocIncluded = false; + bool ownMocUnderscoreIncluded = false; + std::string ownMocUnderscoreFile; + std::string ownMocHeaderFile; + + std::string::size_type matchOffset = 0; + // 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 + if (strstr(contentsString.c_str(), "moc") != CM_NULLPTR) { + // Iterate over all included moc files + const char* contentChars = contentsString.c_str(); + while (mocIncludeRegExp.find(contentChars)) { + const std::string currentMoc = mocIncludeRegExp.match(1); + // Basename of the current moc include + std::string basename = + cmsys::SystemTools::GetFilenameWithoutLastExtension(currentMoc); + + // If the moc include is of the moc_foo.cpp style we expect + // the Q_OBJECT class declaration in a header file. + // If the moc include is of the foo.moc style we need to look for + // a Q_OBJECT macro in the current source file, if it contains the + // macro we generate the moc file from the source file. + if (cmHasLiteralPrefix(basename, "moc_")) { + // Include: moc_FOO.cxx + // basename should be the part of the moc filename used for + // finding the correct header, so we need to remove the moc_ part + basename = basename.substr(4); + const std::string mocSubDir = + extractSubDir(scannedFileAbsPath, currentMoc); + const std::string headerToMoc = findMatchingHeader( + scannedFileAbsPath, mocSubDir, basename, headerExtensions); + + if (!headerToMoc.empty()) { + includedMocs[headerToMoc] = currentMoc; + if (relaxed && (basename == scannedFileBasename)) { + ownMocUnderscoreIncluded = true; + ownMocUnderscoreFile = currentMoc; + ownMocHeaderFile = headerToMoc; + } + } else { + std::ostringstream err; + err << "AUTOGEN: error: " << absFilename << "\n" + << "The file includes the moc file \"" << currentMoc + << "\", but could not find header \"" << basename << '{' + << this->JoinExts(headerExtensions) << "}\" "; + if (mocSubDir.empty()) { + err << "in " << scannedFileAbsPath << "\n"; + } else { + err << "neither in " << scannedFileAbsPath << " nor in " + << mocSubDir << "\n"; + } + this->LogError(err.str()); + return false; + } + } else { + // Include: FOO.moc + std::string fileToMoc; + if (relaxed) { + // Mode: Relaxed + if (!requiresMoc || basename != scannedFileBasename) { + const std::string mocSubDir = + extractSubDir(scannedFileAbsPath, currentMoc); + const std::string headerToMoc = findMatchingHeader( + scannedFileAbsPath, mocSubDir, basename, headerExtensions); + if (!headerToMoc.empty()) { + // This is for KDE4 compatibility: + fileToMoc = headerToMoc; + if (!requiresMoc && basename == scannedFileBasename) { + std::ostringstream err; + err << "AUTOMOC: warning: " << absFilename << "\n" + << "The file includes the moc file \"" << currentMoc + << "\", but does not contain a " << macroName + << " macro. Running moc on " + << "\"" << headerToMoc << "\" ! Include \"moc_" << basename + << ".cpp\" for a compatibility with " + "strict mode (see CMAKE_AUTOMOC_RELAXED_MODE).\n"; + this->LogWarning(err.str()); + } else { + std::ostringstream err; + err << "AUTOMOC: warning: " << absFilename << "\n" + << "The file includes the moc file \"" << currentMoc + << "\" instead of \"moc_" << basename + << ".cpp\". Running moc on " + << "\"" << headerToMoc << "\" ! Include \"moc_" << basename + << ".cpp\" for compatibility with " + "strict mode (see CMAKE_AUTOMOC_RELAXED_MODE).\n"; + this->LogWarning(err.str()); + } + } else { + std::ostringstream err; + err << "AUTOMOC: error: " << absFilename << "\n" + << "The file includes the moc file \"" << currentMoc + << "\", which seems to be the moc file from a different " + "source file. CMake also could not find a matching " + "header.\n"; + this->LogError(err.str()); + return false; + } + } else { + // Include self + fileToMoc = absFilename; + ownDotMocIncluded = true; + } + } else { + // Mode: Strict + if (basename != scannedFileBasename) { + // Don't allow FOO.moc include other than self in strict mode + std::ostringstream err; + err << "AUTOMOC: error: " << absFilename << "\n" + << "The file includes the moc file \"" << currentMoc + << "\", which seems to be the moc file from a different " + "source file. This is not supported. Include \"" + << scannedFileBasename + << ".moc\" to run moc on this source file.\n"; + this->LogError(err.str()); + return false; + } else { + // Include self + fileToMoc = absFilename; + ownDotMocIncluded = true; + } + } + if (!fileToMoc.empty()) { + includedMocs[fileToMoc] = currentMoc; + } + } + // Forward content pointer + contentChars += mocIncludeRegExp.end(); + } + } + + // 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 (requiresMoc && !ownDotMocIncluded) { + if (relaxed && ownMocUnderscoreIncluded) { + // 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 << "\". Running moc on " + << "\"" << absFilename << "\" ! Better include \"" + << scannedFileBasename + << ".moc\" for compatibility with " + "strict mode (see CMAKE_AUTOMOC_RELAXED_MODE).\n"; + this->LogWarning(err.str()); + + // Use scanned source file instead of scanned header file as moc source + includedMocs[absFilename] = ownMocUnderscoreFile; + includedMocs.erase(ownMocHeaderFile); + } else { + // Otherwise always error out since it will not compile: + std::ostringstream err; + err << "AUTOMOC: error: " << absFilename << "\n" + << "The file contains a " << macroName + << " macro, but does not include " + << "\"" << scannedFileBasename << ".moc\" !\n"; + this->LogError(err.str()); + return false; + } + } + + return true; +} + void cmQtAutoGenerators::ParseForUic( const std::string& absFilename, std::map<std::string, std::vector<std::string> >& includedUis) diff --git a/Source/cmQtAutoGenerators.h b/Source/cmQtAutoGenerators.h index 24960a5..f6fbed3 100644 --- a/Source/cmQtAutoGenerators.h +++ b/Source/cmQtAutoGenerators.h @@ -58,6 +58,12 @@ private: const std::vector<std::string>& headerExtensions, std::map<std::string, std::string>& includedMocs, std::map<std::string, std::vector<std::string> >& includedUis); + bool ParseSourceFile( + const std::string& absFilename, + const std::vector<std::string>& headerExtensions, + std::map<std::string, std::string>& includedMocs, + std::map<std::string, std::vector<std::string> >& includedUis, + bool relaxed); void SearchHeadersForCppFile( const std::string& absFilename, const std::vector<std::string>& headerExtensions, |