sammccall created this revision.
sammccall added reviewers: kbobyrev, ilya-biryukov.
Herald added subscribers: cfe-commits, usaxena95, kadircet, arphaman, jkorous, 
MaskRay.
Herald added a project: clang.

Fixes https://github.com/clangd/clangd/issues/211
Fixes https://github.com/clangd/clangd/issues/178

No tests - this is hard to test, and basically impossible to verify what we want
(this produces compile commands that work on a real mac with recent toolchain)

(Need someone on mac to verify it actually fixes these!)


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D70863

Files:
  clang-tools-extra/clangd/GlobalCompilationDatabase.cpp

Index: clang-tools-extra/clangd/GlobalCompilationDatabase.cpp
===================================================================
--- clang-tools-extra/clangd/GlobalCompilationDatabase.cpp
+++ clang-tools-extra/clangd/GlobalCompilationDatabase.cpp
@@ -18,7 +18,9 @@
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/Support/FileSystem.h"
+#include "llvm/Support/FileUtilities.h"
 #include "llvm/Support/Path.h"
+#include "llvm/Support/Program.h"
 #include <string>
 #include <tuple>
 #include <vector>
@@ -63,14 +65,86 @@
 
 } // namespace
 
-static std::string getFallbackClangPath() {
-  static int Dummy;
-  std::string ClangdExecutable =
-      llvm::sys::fs::getMainExecutable("clangd", (void *)&Dummy);
-  SmallString<128> ClangPath;
-  ClangPath = llvm::sys::path::parent_path(ClangdExecutable);
-  llvm::sys::path::append(ClangPath, "clang");
-  return ClangPath.str();
+// On Mac, `which clang` is /usr/bin/clang. It runs `xcrun clang`, which knows
+// where the real clang is kept. We need to do the same thing,
+// because cc1 (not the driver!) will find libc++ relative to argv[0].
+static llvm::Optional<std::string> getMacClangPath() {
+#ifndef __APPLE__
+  return llvm::None;
+#endif
+
+  auto Xcrun = llvm::sys::findProgramByName("xcrun");
+  if (!Xcrun) {
+    log("Couldn't find xcrun. Hopefully you have a non-apple toolchain...");
+    return llvm::None;
+  }
+  llvm::SmallString<64> OutFile;
+  llvm::sys::fs::createTemporaryFile("clangd-xcrun", "", OutFile);
+  llvm::FileRemover OutRemover(OutFile);
+  llvm::Optional<llvm::StringRef> Redirects[3] = {
+      /*stdin=*/{""}, /*stdout=*/{OutFile}, /*stderr=*/{""}};
+  vlog("Invoking {0} to find fallback clang path", *Xcrun);
+  int Ret = llvm::sys::ExecuteAndWait(*Xcrun, {"xcrun", "--find", "clang"},
+                                      /*Env=*/llvm::None, Redirects,
+                                      /*SecondsToWait=*/10);
+  if (Ret != 0) {
+    log("xcrun exists but failed with code {0}. "
+        "If you have a non-apple toolchain, this is OK. "
+        "Otherwise, try xcode-select --install.",
+        Ret);
+    return llvm::None;
+  }
+
+  auto Buf = llvm::MemoryBuffer::getFile(OutFile);
+  if (!Buf) {
+    log("Can't read xcrun output: {0}", Buf.getError().message());
+    return llvm::None;
+  }
+  StringRef Path = Buf->get()->getBuffer().trim();
+  if (Path.empty()) {
+    log("xcrun produced no output");
+    return llvm::None;
+  }
+  return Path.str();
+}
+
+// Resolve symlinks if possible.
+static std::string resolve(std::string Path) {
+  llvm::SmallString<128> Resolved;
+  if (llvm::sys::fs::real_path(Path, Resolved))
+    return Path; // On error;
+  return Resolved.str();
+}
+
+// Get a plausible `clang` to use in the fallback compile command.
+// FIXME: should we also use this if compile_commands.json has just `clang`?
+static StringRef getFallbackClangPath() {
+  static const std::string &MemoizedFallbackPath = [&]() -> std::string {
+    // The driver and/or cc1 sometimes depend on the binary name to compute
+    // useful things like the standard library location.
+    // We need to emulate what clang on this system is likely to see.
+    // cc1 in particular looks at the "real path" of the running process, and
+    // so if /usr/bin/clang is a symlink, it sees the resolved path.
+    // clangd doesn't have that luxury, so we resolve symlinks ourselves.
+
+    // /usr/bin/clang on a mac is a program that redirects to the right clang.
+    // We resolve it as if it were a symlink.
+    if (auto MacClang = getMacClangPath())
+      return resolve(std::move(*MacClang));
+    // On other platforms, just look for compilers on the PATH.
+    for (const char* Name : {"clang", "gcc", "ccc"})
+      if (auto PathCC = llvm::sys::findProgramByName(Name))
+        return resolve(std::move(*PathCC));
+    // Fallback: a nonexistent 'clang' binary next to clangd.
+    static int Dummy;
+    std::string ClangdExecutable =
+        llvm::sys::fs::getMainExecutable("clangd", (void *)&Dummy);
+    SmallString<128> ClangPath;
+    ClangPath = llvm::sys::path::parent_path(ClangdExecutable);
+    llvm::sys::path::append(ClangPath, "clang");
+    return ClangPath.str();
+  }();
+  return MemoizedFallbackPath;
 }
 
 tooling::CompileCommand
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to