Forráskód Böngészése

AUTOGEN: Generators: Introduce unified ParseSourceFile

The new cmQtAutoGenerators::ParseSourceFile method combines the
functionality of ParseCppFile and StrictParseCppFile in a single method
to avoid code duplication. Relaxed or strict mode is selected by an
additional bool argument.
Sebastian Holtermann 8 éve
szülő
commit
0b1527ff3d
2 módosított fájl, 214 hozzáadás és 0 törlés
  1. 208 0
      Source/cmQtAutoGenerators.cxx
  2. 6 0
      Source/cmQtAutoGenerators.h

+ 208 - 0
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)

+ 6 - 0
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,