Author: Aleksandr Platonov
Date: 2020-11-26T15:08:26+03:00
New Revision: 1ca174b6420a49bcd3331d6f86e237b627163597

URL: 
https://github.com/llvm/llvm-project/commit/1ca174b6420a49bcd3331d6f86e237b627163597
DIFF: 
https://github.com/llvm/llvm-project/commit/1ca174b6420a49bcd3331d6f86e237b627163597.diff

LOG: [clangd][query-driver] Extract target

In some cases system includes extractions is not enough, we also need target 
specific defines.
The problems appears when clang default target is not the same as toolchain's 
one (GCC cross-compiler, MinGW on Windows).
After this patch `query-driver` also extracts target and adds 
`--target=<extracted target>` compile option.

Reviewed By: sammccall

Differential Revision: https://reviews.llvm.org/D92012

Added: 
    

Modified: 
    clang-tools-extra/clangd/QueryDriverDatabase.cpp
    clang-tools-extra/clangd/test/system-include-extractor.test

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clangd/QueryDriverDatabase.cpp 
b/clang-tools-extra/clangd/QueryDriverDatabase.cpp
index 9d731f5563cf..d59263e73994 100644
--- a/clang-tools-extra/clangd/QueryDriverDatabase.cpp
+++ b/clang-tools-extra/clangd/QueryDriverDatabase.cpp
@@ -33,6 +33,9 @@
 #include "support/Logger.h"
 #include "support/Path.h"
 #include "support/Trace.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/Basic/TargetOptions.h"
 #include "clang/Driver/Types.h"
 #include "clang/Tooling/CompilationDatabase.h"
 #include "llvm/ADT/DenseMap.h"
@@ -56,55 +59,102 @@ namespace clang {
 namespace clangd {
 namespace {
 
-std::vector<std::string> parseDriverOutput(llvm::StringRef Output) {
+struct DriverInfo {
   std::vector<std::string> SystemIncludes;
+  std::string Target;
+};
+
+bool isValidTarget(llvm::StringRef Triple) {
+  std::shared_ptr<TargetOptions> TargetOpts(new TargetOptions);
+  TargetOpts->Triple = Triple.str();
+  DiagnosticsEngine Diags(new DiagnosticIDs, new DiagnosticOptions,
+                          new IgnoringDiagConsumer);
+  IntrusiveRefCntPtr<TargetInfo> Target =
+      TargetInfo::CreateTargetInfo(Diags, TargetOpts);
+  return bool(Target);
+}
+
+llvm::Optional<DriverInfo> parseDriverOutput(llvm::StringRef Output) {
+  DriverInfo Info;
   const char SIS[] = "#include <...> search starts here:";
   const char SIE[] = "End of search list.";
+  const char TS[] = "Target: ";
   llvm::SmallVector<llvm::StringRef, 8> Lines;
   Output.split(Lines, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false);
 
-  auto StartIt = llvm::find_if(
-      Lines, [SIS](llvm::StringRef Line) { return Line.trim() == SIS; });
-  if (StartIt == Lines.end()) {
+  enum {
+    Initial,            // Initial state: searching for target or includes 
list.
+    IncludesExtracting, // Includes extracting.
+    Done                // Includes and target extraction done.
+  } State = Initial;
+  bool SeenIncludes = false;
+  bool SeenTarget = false;
+  for (auto *It = Lines.begin(); State != Done && It != Lines.end(); ++It) {
+    auto Line = *It;
+    switch (State) {
+    case Initial:
+      if (!SeenIncludes && Line.trim() == SIS) {
+        SeenIncludes = true;
+        State = IncludesExtracting;
+      } else if (!SeenTarget && Line.trim().startswith(TS)) {
+        SeenTarget = true;
+        llvm::StringRef TargetLine = Line.trim();
+        TargetLine.consume_front(TS);
+        // Only detect targets that clang understands
+        if (!isValidTarget(TargetLine)) {
+          elog("System include extraction: invalid target \"{0}\", ignoring",
+               TargetLine);
+        } else {
+          Info.Target = TargetLine.str();
+          vlog("System include extraction: target extracted: \"{0}\"",
+               TargetLine);
+        }
+      }
+      break;
+    case IncludesExtracting:
+      if (Line.trim() == SIE) {
+        State = SeenTarget ? Done : Initial;
+      } else {
+        Info.SystemIncludes.push_back(Line.trim().str());
+        vlog("System include extraction: adding {0}", Line);
+      }
+      break;
+    default:
+      llvm_unreachable("Impossible state of the driver output parser");
+      break;
+    }
+  }
+  if (!SeenIncludes) {
     elog("System include extraction: start marker not found: {0}", Output);
-    return {};
+    return llvm::None;
   }
-  ++StartIt;
-  const auto EndIt =
-      llvm::find_if(llvm::make_range(StartIt, Lines.end()),
-                    [SIE](llvm::StringRef Line) { return Line.trim() == SIE; 
});
-  if (EndIt == Lines.end()) {
+  if (State == IncludesExtracting) {
     elog("System include extraction: end marker missing: {0}", Output);
-    return {};
+    return llvm::None;
   }
-
-  for (llvm::StringRef Line : llvm::make_range(StartIt, EndIt)) {
-    SystemIncludes.push_back(Line.trim().str());
-    vlog("System include extraction: adding {0}", Line);
-  }
-  return SystemIncludes;
+  return std::move(Info);
 }
 
-std::vector<std::string>
-extractSystemIncludes(PathRef Driver, llvm::StringRef Lang,
-                      llvm::ArrayRef<std::string> CommandLine,
-                      const llvm::Regex &QueryDriverRegex) {
-  trace::Span Tracer("Extract system includes");
+llvm::Optional<DriverInfo>
+extractSystemIncludesAndTarget(PathRef Driver, llvm::StringRef Lang,
+                               llvm::ArrayRef<std::string> CommandLine,
+                               const llvm::Regex &QueryDriverRegex) {
+  trace::Span Tracer("Extract system includes and target");
   SPAN_ATTACH(Tracer, "driver", Driver);
   SPAN_ATTACH(Tracer, "lang", Lang);
 
   if (!QueryDriverRegex.match(Driver)) {
     vlog("System include extraction: not allowed driver {0}", Driver);
-    return {};
+    return llvm::None;
   }
 
   if (!llvm::sys::fs::exists(Driver)) {
     elog("System include extraction: {0} does not exist.", Driver);
-    return {};
+    return llvm::None;
   }
   if (!llvm::sys::fs::can_execute(Driver)) {
     elog("System include extraction: {0} is not executable.", Driver);
-    return {};
+    return llvm::None;
   }
 
   llvm::SmallString<128> StdErrPath;
@@ -113,7 +163,7 @@ extractSystemIncludes(PathRef Driver, llvm::StringRef Lang,
     elog("System include extraction: failed to create temporary file with "
          "error {0}",
          EC.message());
-    return {};
+    return llvm::None;
   }
   auto CleanUp = llvm::make_scope_exit(
       [&StdErrPath]() { llvm::sys::fs::remove(StdErrPath); });
@@ -158,21 +208,24 @@ extractSystemIncludes(PathRef Driver, llvm::StringRef 
Lang,
     elog("System include extraction: driver execution failed with return code: 
"
          "{0}. Args: ['{1}']",
          llvm::to_string(RC), llvm::join(Args, "', '"));
-    return {};
+    return llvm::None;
   }
 
   auto BufOrError = llvm::MemoryBuffer::getFile(StdErrPath);
   if (!BufOrError) {
     elog("System include extraction: failed to read {0} with error {1}",
          StdErrPath, BufOrError.getError().message());
-    return {};
+    return llvm::None;
   }
 
-  auto Includes = parseDriverOutput(BufOrError->get()->getBuffer());
-  log("System include extractor: successfully executed {0}, got includes: "
-      "\"{1}\"",
-      Driver, llvm::join(Includes, ", "));
-  return Includes;
+  llvm::Optional<DriverInfo> Info =
+      parseDriverOutput(BufOrError->get()->getBuffer());
+  if (!Info)
+    return llvm::None;
+  log("System includes extractor: successfully executed {0}\n\tgot includes: "
+      "\"{1}\"\n\tgot target: \"{2}\"",
+      Driver, llvm::join(Info->SystemIncludes, ", "), Info->Target);
+  return Info;
 }
 
 tooling::CompileCommand &
@@ -186,6 +239,19 @@ addSystemIncludes(tooling::CompileCommand &Cmd,
   return Cmd;
 }
 
+tooling::CompileCommand &setTarget(tooling::CompileCommand &Cmd,
+                                   const std::string &Target) {
+  if (!Target.empty()) {
+    // We do not want to override existing target with extracted one.
+    for (llvm::StringRef Arg : Cmd.CommandLine) {
+      if (Arg == "-target" || Arg.startswith("--target="))
+        return Cmd;
+    }
+    Cmd.CommandLine.push_back("--target=" + Target);
+  }
+  return Cmd;
+}
+
 /// Converts a glob containing only ** or * into a regex.
 std::string convertGlobToRegex(llvm::StringRef Glob) {
   std::string RegText;
@@ -268,13 +334,14 @@ class QueryDriverDatabase : public 
GlobalCompilationDatabase {
     llvm::SmallString<128> Driver(Cmd->CommandLine.front());
     llvm::sys::fs::make_absolute(Cmd->Directory, Driver);
 
-    std::vector<std::string> SystemIncludes =
-        QueriedDrivers.get(/*Key=*/(Driver + ":" + Lang).str(), [&] {
-          return extractSystemIncludes(Driver, Lang, Cmd->CommandLine,
-                                       QueryDriverRegex);
-        });
-
-    return addSystemIncludes(*Cmd, SystemIncludes);
+    if (auto Info =
+            QueriedDrivers.get(/*Key=*/(Driver + ":" + Lang).str(), [&] {
+              return extractSystemIncludesAndTarget(
+                  Driver, Lang, Cmd->CommandLine, QueryDriverRegex);
+            })) {
+      setTarget(addSystemIncludes(*Cmd, Info->SystemIncludes), Info->Target);
+    }
+    return Cmd;
   }
 
   llvm::Optional<ProjectInfo> getProjectInfo(PathRef File) const override {
@@ -283,7 +350,7 @@ class QueryDriverDatabase : public 
GlobalCompilationDatabase {
 
 private:
   // Caches includes extracted from a driver. Key is driver:lang.
-  Memoize<llvm::StringMap<std::vector<std::string>>> QueriedDrivers;
+  Memoize<llvm::StringMap<llvm::Optional<DriverInfo>>> QueriedDrivers;
   llvm::Regex QueryDriverRegex;
 
   std::unique_ptr<GlobalCompilationDatabase> Base;

diff  --git a/clang-tools-extra/clangd/test/system-include-extractor.test 
b/clang-tools-extra/clangd/test/system-include-extractor.test
index 03cb54069d6e..59989be6ca6c 100644
--- a/clang-tools-extra/clangd/test/system-include-extractor.test
+++ b/clang-tools-extra/clangd/test/system-include-extractor.test
@@ -12,6 +12,7 @@
 # RUN: echo '[ -z "${args##*"-isysroot=/isysroot"*}" ] || exit' >> 
%t.dir/my_driver.sh
 # RUN: echo 'echo " $* " | grep " --sysroot /my/sysroot/path " || exit' >> 
%t.dir/my_driver.sh
 # RUN: echo 'echo line to ignore >&2' >> %t.dir/my_driver.sh
+# RUN: echo 'printf "Target: arm-linux-gnueabihf\r\n" >&2' >> 
%t.dir/my_driver.sh
 # RUN: echo 'printf "#include <...> search starts here:\r\n" >&2' >> 
%t.dir/my_driver.sh
 # RUN: echo 'echo %t.dir/my/dir/ >&2' >> %t.dir/my_driver.sh
 # RUN: echo 'echo %t.dir/my/dir2/ >&2' >> %t.dir/my_driver.sh
@@ -45,7 +46,7 @@
       "uri": "file://INPUT_DIR/the-file.cpp",
       "languageId":"cpp",
       "version":1,
-      "text":"#include <a.h>\n#include <b.h>"
+      "text":"#include <a.h>\n#include <b.h>\n#if !defined(__ARM_ARCH) || 
!defined(__gnu_linux__)\n#error \"Invalid target\"\n#endif"
     }
   }
 }


        
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to