浏览代码

automoc: add a StrictParseCppFile(), which is only qmake-compatible

ParseCppFile() is the one which is automoc4/KDE4-compatible, and which
becomes a bit crowded. By separating these two it is easier to ensure
that the strict one doesn't get broken accidentially.

Alex
Alex Neundorf 14 年之前
父节点
当前提交
9c0df72dc4
共有 2 个文件被更改,包括 125 次插入6 次删除
  1. 122 6
      Source/cmQtAutomoc.cxx
  2. 3 0
      Source/cmQtAutomoc.h

+ 122 - 6
Source/cmQtAutomoc.cxx

@@ -495,7 +495,14 @@ bool cmQtAutomoc::RunAutomocQt4()
       {
       std::cout << "AUTOMOC: Checking " << absFilename << std::endl;
       }
-    this->ParseCppFile(absFilename, headerExtensions, includedMocs);
+    if (this->QtMajorVersion == "4")
+      {
+      this->ParseCppFile(absFilename, headerExtensions, includedMocs);
+      }
+    else if (this->QtMajorVersion == "5")
+      {
+      this->StrictParseCppFile(absFilename, headerExtensions, includedMocs);
+      }
     this->SearchHeadersForCppFile(absFilename, headerExtensions, headerFiles);
     }
 
@@ -669,8 +676,7 @@ void cmQtAutomoc::ParseCppFile(const std::string& absFilename,
         if (basename != scannedFileBasename)
           {
           bool fail = true;
-          if ((this->QtMajorVersion == "4")
-                                    && (basename == scannedFileBasename +"_p"))
+          if (basename == scannedFileBasename+"_p")
             {
             std::string mocSubDir = extractSubDir(absPath, currentMoc);
             std::string headerToMoc = findMatchingHeader(
@@ -718,7 +724,7 @@ void cmQtAutomoc::ParseCppFile(const std::string& absFilename,
   // But warn, since this is not how it is supposed to be used.
   if ((dotMocIncluded == false) && (containsQ_OBJECT(contentsString)))
     {
-    if ((this->QtMajorVersion == "4") && (mocUnderscoreIncluded == true))
+    if (mocUnderscoreIncluded == true)
       {
       // this is for KDE4 compatibility:
       std::cerr << "AUTOMOC: warning: " << absFilename << ": The file "
@@ -746,8 +752,7 @@ void cmQtAutomoc::ParseCppFile(const std::string& absFilename,
   // if only the .moc file is included and we are in compatibility mode,
   // check whether maybe the header must actually be mocced, e.g. because it
   // might use the Q_PRIVATE_SLOT macro:
-  if ((dotMocIncluded == true) && (mocUnderscoreIncluded == false)
-                               && (this->QtMajorVersion == "4"))
+  if ((dotMocIncluded == true) && (mocUnderscoreIncluded == false))
     {
     std::string ownHeader=findMatchingHeader(absPath, "", scannedFileBasename,
                                              headerExtensions);
@@ -776,6 +781,117 @@ void cmQtAutomoc::ParseCppFile(const std::string& absFilename,
 }
 
 
+void cmQtAutomoc::StrictParseCppFile(const std::string& absFilename,
+                              const std::list<std::string>& headerExtensions,
+                              std::map<std::string, std::string>& includedMocs)
+{
+  cmsys::RegularExpression mocIncludeRegExp(
+              "[\n][ \t]*#[ \t]*include[ \t]+"
+              "[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]");
+
+  const std::string contentsString = this->ReadAll(absFilename);
+  if (contentsString.empty())
+    {
+    std::cerr << "AUTOMOC: warning: " << absFilename << ": file is empty\n"
+              << std::endl;
+    return;
+    }
+  const std::string absPath = cmsys::SystemTools::GetFilenamePath(
+                   cmsys::SystemTools::GetRealPath(absFilename.c_str())) + '/';
+  const std::string scannedFileBasename = cmsys::SystemTools::
+                                  GetFilenameWithoutLastExtension(absFilename);
+
+  bool dotMocIncluded = false;
+
+  std::string::size_type matchOffset = 0;
+  // first a simply 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") != NULL)
+                                    && (mocIncludeRegExp.find(contentsString)))
+    {
+    // for every moc include in the file
+    do
+      {
+      const std::string currentMoc = mocIncludeRegExp.match(1);
+
+      std::string basename = cmsys::SystemTools::
+                                   GetFilenameWithoutLastExtension(currentMoc);
+      const bool mocUnderscoreStyle = this->StartsWith(basename, "moc_");
+
+      // 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 (mocUnderscoreStyle)
+        {
+        // 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);
+        std::string mocSubDir = extractSubDir(absPath, currentMoc);
+        std::string headerToMoc = findMatchingHeader(
+                               absPath, mocSubDir, basename, headerExtensions);
+
+        if (!headerToMoc.empty())
+          {
+          includedMocs[headerToMoc] = currentMoc;
+          }
+        else
+          {
+          std::cerr << "AUTOMOC: error: " << absFilename << " The file "
+                    << "includes the moc file \"" << currentMoc << "\", "
+                    << "but could not find header \"" << basename
+                    << '{' << this->Join(headerExtensions, ',') << "}\" ";
+          if (mocSubDir.empty())
+            {
+            std::cerr << "in " << absPath << "\n" << std::endl;
+            }
+          else
+            {
+            std::cerr << "neither in " << absPath
+                      << " nor in " << mocSubDir << "\n" << std::endl;
+            }
+
+          ::exit(EXIT_FAILURE);
+          }
+        }
+      else
+        {
+        if (basename != scannedFileBasename)
+          {
+          std::cerr <<"AUTOMOC: error: " << absFilename << ": 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" << std::endl;
+          ::exit(EXIT_FAILURE);
+          }
+        dotMocIncluded = true;
+        includedMocs[absFilename] = currentMoc;
+        }
+      matchOffset += mocIncludeRegExp.end();
+      } while(mocIncludeRegExp.find(contentsString.c_str() + matchOffset));
+    }
+
+  // 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 ((dotMocIncluded == false) && (containsQ_OBJECT(contentsString)))
+    {
+    // otherwise always error out since it will not compile:
+    std::cerr << "AUTOMOC: error: " << absFilename << ": The file "
+              << "contains a Q_OBJECT macro, but does not include "
+              << "\"" << scannedFileBasename << ".moc\" !\n"
+              << std::endl;
+    ::exit(EXIT_FAILURE);
+    }
+
+}
+
+
 void cmQtAutomoc::SearchHeadersForCppFile(const std::string& absFilename,
                                 const std::list<std::string>& headerExtensions,
                                 std::set<std::string>& absHeaders)

+ 3 - 0
Source/cmQtAutomoc.h

@@ -41,6 +41,9 @@ private:
   void ParseCppFile(const std::string& absFilename,
                     const std::list<std::string>& headerExtensions,
                     std::map<std::string, std::string>& includedMocs);
+  void StrictParseCppFile(const std::string& absFilename,
+                          const std::list<std::string>& headerExtensions,
+                          std::map<std::string, std::string>& includedMocs);
   void SearchHeadersForCppFile(const std::string& absFilename,
                                const std::list<std::string>& headerExtensions,
                                std::set<std::string>& absHeaders);