Browse Source

cmGccDepfileReader: Rework helper code

Fix some of the semantics of the depfile, add error handling, and
refactor cmGccDepfileLexerHelper.
Kyle Edwards 5 years ago
parent
commit
946adadd40

+ 1 - 1
Source/LexerParser/cmGccDepfileLexer.cxx

@@ -994,7 +994,7 @@ case 5:
 YY_RULE_SETUP
 {
                          // A line continuation ends the current file name.
-                         yyextra->newDependency();
+                         yyextra->newRuleOrDependency();
                        }
 	YY_BREAK
 case 6:

+ 1 - 1
Source/LexerParser/cmGccDepfileLexer.in.l

@@ -42,7 +42,7 @@ NEWLINE \r?\n
                        }
 {WSPACE}*\\{NEWLINE}   {
                          // A line continuation ends the current file name.
-                         yyextra->newDependency();
+                         yyextra->newRuleOrDependency();
                        }
 {NEWLINE}              {
                          // A newline ends the current file name and the current rule.

+ 22 - 11
Source/cmGccDepfileLexerHelper.cxx

@@ -27,23 +27,30 @@ bool cmGccDepfileLexerHelper::readFile(const char* filePath)
   if (!file) {
     return false;
   }
-  newEntry();
+  this->newEntry();
   yyscan_t scanner;
   cmGccDepfile_yylex_init(&scanner);
   cmGccDepfile_yyset_extra(this, scanner);
   cmGccDepfile_yyrestart(file, scanner);
   cmGccDepfile_yylex(scanner);
   cmGccDepfile_yylex_destroy(scanner);
-  sanitizeContent();
+  this->sanitizeContent();
   fclose(file);
-  return true;
+  return this->HelperState != State::Failed;
 }
 
 void cmGccDepfileLexerHelper::newEntry()
 {
+  if (this->HelperState == State::Rule && !this->Content.empty()) {
+    if (!this->Content.back().rules.empty() &&
+        !this->Content.back().rules.back().empty()) {
+      this->HelperState = State::Failed;
+    }
+    return;
+  }
   this->HelperState = State::Rule;
   this->Content.emplace_back();
-  newRule();
+  this->newRule();
 }
 
 void cmGccDepfileLexerHelper::newRule()
@@ -56,20 +63,22 @@ void cmGccDepfileLexerHelper::newRule()
 
 void cmGccDepfileLexerHelper::newDependency()
 {
-  // printf("NEW DEP\n");
+  if (this->HelperState == State::Failed) {
+    return;
+  }
   this->HelperState = State::Dependency;
-  if (this->Content.back().paths.empty() ||
-      !this->Content.back().paths.back().empty()) {
-    this->Content.back().paths.emplace_back();
+  auto& entry = this->Content.back();
+  if (entry.paths.empty() || !entry.paths.back().empty()) {
+    entry.paths.emplace_back();
   }
 }
 
 void cmGccDepfileLexerHelper::newRuleOrDependency()
 {
   if (this->HelperState == State::Rule) {
-    newRule();
-  } else {
-    newDependency();
+    this->newRule();
+  } else if (this->HelperState == State::Dependency) {
+    this->newDependency();
   }
 }
 
@@ -93,6 +102,8 @@ void cmGccDepfileLexerHelper::addToCurrentPath(const char* s)
       }
       dst = &dep->paths.back();
     } break;
+    case State::Failed:
+      return;
   }
   dst->append(s);
 }

+ 2 - 1
Source/cmGccDepfileLexerHelper.h

@@ -29,7 +29,8 @@ private:
   enum class State
   {
     Rule,
-    Dependency
+    Dependency,
+    Failed,
   };
   State HelperState = State::Rule;
 };

+ 5 - 4
Source/cmGccDepfileReader.cxx

@@ -5,14 +5,15 @@
 #include <type_traits>
 #include <utility>
 
+#include <cm/optional>
+
 #include "cmGccDepfileLexerHelper.h"
 
-cmGccDepfileContent cmReadGccDepfile(const char* filePath)
+cm::optional<cmGccDepfileContent> cmReadGccDepfile(const char* filePath)
 {
-  cmGccDepfileContent result;
   cmGccDepfileLexerHelper helper;
   if (helper.readFile(filePath)) {
-    result = std::move(helper).extractContent();
+    return cm::make_optional(std::move(helper).extractContent());
   }
-  return result;
+  return cm::nullopt;
 }

+ 3 - 1
Source/cmGccDepfileReader.h

@@ -2,6 +2,8 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #pragma once
 
+#include <cm/optional>
+
 #include "cmGccDepfileReaderTypes.h"
 
-cmGccDepfileContent cmReadGccDepfile(const char* filePath);
+cm::optional<cmGccDepfileContent> cmReadGccDepfile(const char* filePath);

+ 4 - 4
Source/cmQtAutoMocUic.cxx

@@ -15,6 +15,7 @@
 #include <vector>
 
 #include <cm/memory>
+#include <cm/optional>
 #include <cm/string_view>
 #include <cmext/algorithm>
 
@@ -26,7 +27,6 @@
 #include "cmCryptoHash.h"
 #include "cmFileTime.h"
 #include "cmGccDepfileReader.h"
-#include "cmGccDepfileReaderTypes.h"
 #include "cmGeneratedFileStream.h"
 #include "cmQtAutoGen.h"
 #include "cmQtAutoGenerator.h"
@@ -2841,14 +2841,14 @@ bool cmQtAutoMocUicT::CreateDirectories()
 std::vector<std::string> cmQtAutoMocUicT::dependenciesFromDepFile(
   const char* filePath)
 {
-  cmGccDepfileContent content = cmReadGccDepfile(filePath);
-  if (content.empty()) {
+  auto const content = cmReadGccDepfile(filePath);
+  if (!content || content->empty()) {
     return {};
   }
 
   // Moc outputs a depfile with exactly one rule.
   // Discard the rule and return the dependencies.
-  return content.front().paths;
+  return content->front().paths;
 }
 
 void cmQtAutoMocUicT::Abort(bool error)

+ 16 - 5
Tests/CMakeLib/testGccDepfileReader.cxx

@@ -5,6 +5,8 @@
 #include <utility>
 #include <vector>
 
+#include <cm/optional>
+
 #include "cmsys/FStream.hxx"
 
 #include "cmGccDepfileReader.h"
@@ -112,17 +114,26 @@ int testGccDepfileReader(int argc, char* argv[])
 
   std::string dataDirPath = argv[1];
   dataDirPath += "/testGccDepfileReader_data";
-  const int numberOfTestFiles = 3;
+  const int numberOfTestFiles = 7; // 6th file doesn't exist
   for (int i = 1; i <= numberOfTestFiles; ++i) {
     const std::string base = dataDirPath + "/deps" + std::to_string(i);
     const std::string depfile = base + ".d";
     const std::string plainDepfile = base + ".txt";
     std::cout << "Comparing " << base << " with " << plainDepfile << std::endl;
     const auto actual = cmReadGccDepfile(depfile.c_str());
-    const auto expected = readPlainDepfile(plainDepfile.c_str());
-    if (!compare(actual, expected)) {
-      dump("actual", actual);
-      dump("expected", expected);
+    if (cmSystemTools::FileExists(plainDepfile)) {
+      if (!actual) {
+        std::cerr << "Reading " << depfile << " should have succeeded\n";
+        return 1;
+      }
+      const auto expected = readPlainDepfile(plainDepfile.c_str());
+      if (!compare(*actual, expected)) {
+        dump("actual", *actual);
+        dump("expected", expected);
+        return 1;
+      }
+    } else if (actual) {
+      std::cerr << "Reading " << depfile << " should have failed\n";
       return 1;
     }
   }

+ 1 - 0
Tests/CMakeLib/testGccDepfileReader_data/deps4.d

@@ -0,0 +1 @@
+invalid

+ 0 - 0
Tests/CMakeLib/testGccDepfileReader_data/deps5.d


+ 2 - 0
Tests/CMakeLib/testGccDepfileReader_data/deps5.txt

@@ -0,0 +1,2 @@
+--RULES--
+--DEPENDENCIES--

+ 6 - 0
Tests/CMakeLib/testGccDepfileReader_data/deps7.d

@@ -0,0 +1,6 @@
+out1 \
+  out2: \
+  in1 \
+  in2
+
+out3: in3

+ 10 - 0
Tests/CMakeLib/testGccDepfileReader_data/deps7.txt

@@ -0,0 +1,10 @@
+--RULES--
+out1
+out2
+--DEPENDENCIES--
+in1
+in2
+--RULES--
+out3
+--DEPENDENCIES--
+in3