[PATCH] D156044: [clangd] Exclude builtin headers from system include extraction

2023-07-25 Thread Sam McCall via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rG0478ef2d366c: [clangd] Exclude builtin headers from system 
include extraction (authored by sammccall).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D156044/new/

https://reviews.llvm.org/D156044

Files:
  clang-tools-extra/clangd/SystemIncludeExtractor.cpp
  clang-tools-extra/clangd/test/system-include-extractor.test

Index: clang-tools-extra/clangd/test/system-include-extractor.test
===
--- clang-tools-extra/clangd/test/system-include-extractor.test
+++ clang-tools-extra/clangd/test/system-include-extractor.test
@@ -10,6 +10,7 @@
 # %temp_dir%/my/dir2 as include search paths.
 # RUN: echo '#!/bin/sh' >> %t.dir/bin/my_driver.sh
 # RUN: echo '[ "$0" = "%t.dir/bin/my_driver.sh" ] || exit' >> %t.dir/bin/my_driver.sh
+# RUN: echo '[ "$1" = "-print-file-name=include" ] && echo "%t.dir/builtin" && exit' >> %t.dir/bin/my_driver.sh
 # RUN: echo 'args="$*"' >> %t.dir/bin/my_driver.sh
 # Check that clangd preserves certain flags like `-nostdinc` from
 # original invocation in compile_commands.json.
@@ -21,6 +22,7 @@
 # RUN: echo 'printf "#include <...> search starts here:\r\n" >&2' >> %t.dir/bin/my_driver.sh
 # RUN: echo 'echo %t.dir/my/dir/ >&2' >> %t.dir/bin/my_driver.sh
 # RUN: echo 'echo %t.dir/my/dir2/ >&2' >> %t.dir/bin/my_driver.sh
+# RUN: echo 'echo %t.dir/builtin >&2' >> %t.dir/bin/my_driver.sh
 # RUN: echo 'printf "End of search list.\r\n" >&2' >> %t.dir/bin/my_driver.sh
 # RUN: chmod +x %t.dir/bin/my_driver.sh
 
@@ -29,6 +31,8 @@
 # RUN: touch %t.dir/my/dir/a.h
 # RUN: mkdir -p %t.dir/my/dir2
 # RUN: touch %t.dir/my/dir2/b.h
+# RUN: mkdir -p %t.dir/builtin
+# RUN: touch %t.dir/builtin/c.h
 
 # Generate a compile_commands.json that will query the mock driver we've
 # created. Which should add a.h and b.h into include search path.
@@ -53,7 +57,7 @@
   "uri": "file://INPUT_DIR/the-file.cpp",
   "languageId":"cpp",
   "version":1,
-  "text":"#include \n#include \n#if !defined(__ARM_ARCH) || !defined(__gnu_linux__)\n#error \"Invalid target\"\n#endif\n"
+  "text":"#include \n#include \n#if !defined(__ARM_ARCH) || !defined(__gnu_linux__)\n#error \"Invalid target\"\n#endif\n#if __has_include()\n#error \"wrong-toolchain builtins\"\n#endif\n"
 }
   }
 }
Index: clang-tools-extra/clangd/SystemIncludeExtractor.cpp
===
--- clang-tools-extra/clangd/SystemIncludeExtractor.cpp
+++ clang-tools-extra/clangd/SystemIncludeExtractor.cpp
@@ -83,17 +83,16 @@
   // Whether certain includes should be part of query.
   bool StandardIncludes = true;
   bool StandardCXXIncludes = true;
-  bool BuiltinIncludes = true;
   // Language to use while querying.
   std::string Lang;
   std::string Sysroot;
   std::string ISysroot;
 
   bool operator==(const DriverArgs &RHS) const {
-return std::tie(Driver, StandardIncludes, StandardCXXIncludes,
-BuiltinIncludes, Lang, Sysroot, ISysroot) ==
+return std::tie(Driver, StandardIncludes, StandardCXXIncludes, Lang,
+Sysroot, ISysroot) ==
std::tie(RHS.Driver, RHS.StandardIncludes, RHS.StandardCXXIncludes,
-RHS.BuiltinIncludes, RHS.Lang, RHS.Sysroot, ISysroot);
+RHS.Lang, RHS.Sysroot, ISysroot);
   }
 
   DriverArgs(const tooling::CompileCommand &Cmd, llvm::StringRef File) {
@@ -120,8 +119,6 @@
 StandardIncludes = false;
   else if (Arg == "-nostdinc++")
 StandardCXXIncludes = false;
-  else if (Arg == "-nobuiltininc")
-BuiltinIncludes = false;
   // Figure out sysroot
   else if (Arg.consume_front("--sysroot")) {
 if (Arg.consume_front("="))
@@ -156,8 +153,6 @@
   Args.push_back("-nostdinc");
 if (!StandardCXXIncludes)
   Args.push_back("-nostdinc++");
-if (!BuiltinIncludes)
-  Args.push_back("-nobuiltininc++");
 if (!Sysroot.empty())
   Args.append({"--sysroot", Sysroot});
 if (!ISysroot.empty())
@@ -190,7 +185,6 @@
 Val.Driver,
 Val.StandardIncludes,
 Val.StandardCXXIncludes,
-Val.BuiltinIncludes,
 Val.Lang,
 Val.Sysroot,
 Val.ISysroot,
@@ -274,6 +268,42 @@
   return std::move(Info);
 }
 
+std::optional run(llvm::ArrayRef Argv,
+   bool OutputIsStderr) {
+  llvm::SmallString<128> OutputPath;
+  if (auto EC = llvm::sys::fs::createTemporaryFile("system-includes", "clangd",
+   OutputPath)) {
+elog("System include extraction: failed to create temporary file with "
+ "error {0}",
+ EC.message());
+return std::nullopt;
+  }
+  auto CleanUp = llvm::make_scope_exit(
+  [&OutputPath]() { llvm::sys::fs::remove(OutputPath); });
+
+  std::optional Redirects[] = {{""}, {""}, {""}};
+  Re

[PATCH] D156044: [clangd] Exclude builtin headers from system include extraction

2023-07-24 Thread Paul Smith via Phabricator via cfe-commits
madscientist added a comment.

(just a note that another way to do this check is to look for some common 
intrinsic file in each directory generated by the compiler driver, and remove 
any directory containing those files.  But, if the `-print-file-name=include` 
trick works reliably that's good too).


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D156044/new/

https://reviews.llvm.org/D156044

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


[PATCH] D156044: [clangd] Exclude builtin headers from system include extraction

2023-07-24 Thread Kadir Cetinkaya via Phabricator via cfe-commits
kadircet accepted this revision.
kadircet added a comment.
This revision is now accepted and ready to land.

thanks a lot!


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D156044/new/

https://reviews.llvm.org/D156044

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


[PATCH] D156044: [clangd] Exclude builtin headers from system include extraction

2023-07-23 Thread Paul Smith via Phabricator via cfe-commits
madscientist added a comment.

I built latest main with this patch applied and it does fix the issues in my 
environment.  My headers that include xmmintrin.h etc. don't throw errors in 
clangd.  Thanks!


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D156044/new/

https://reviews.llvm.org/D156044

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


[PATCH] D156044: [clangd] Exclude builtin headers from system include extraction

2023-07-23 Thread Sam McCall via Phabricator via cfe-commits
sammccall updated this revision to Diff 543291.
sammccall added a comment.

fix incomplete cleanup of dead option


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D156044/new/

https://reviews.llvm.org/D156044

Files:
  clang-tools-extra/clangd/SystemIncludeExtractor.cpp
  clang-tools-extra/clangd/test/system-include-extractor.test

Index: clang-tools-extra/clangd/test/system-include-extractor.test
===
--- clang-tools-extra/clangd/test/system-include-extractor.test
+++ clang-tools-extra/clangd/test/system-include-extractor.test
@@ -10,6 +10,7 @@
 # %temp_dir%/my/dir2 as include search paths.
 # RUN: echo '#!/bin/sh' >> %t.dir/bin/my_driver.sh
 # RUN: echo '[ "$0" = "%t.dir/bin/my_driver.sh" ] || exit' >> %t.dir/bin/my_driver.sh
+# RUN: echo '[ "$1" = "-print-file-name=include" ] && echo "%t.dir/builtin" && exit' >> %t.dir/bin/my_driver.sh
 # RUN: echo 'args="$*"' >> %t.dir/bin/my_driver.sh
 # Check that clangd preserves certain flags like `-nostdinc` from
 # original invocation in compile_commands.json.
@@ -21,6 +22,7 @@
 # RUN: echo 'printf "#include <...> search starts here:\r\n" >&2' >> %t.dir/bin/my_driver.sh
 # RUN: echo 'echo %t.dir/my/dir/ >&2' >> %t.dir/bin/my_driver.sh
 # RUN: echo 'echo %t.dir/my/dir2/ >&2' >> %t.dir/bin/my_driver.sh
+# RUN: echo 'echo %t.dir/builtin >&2' >> %t.dir/bin/my_driver.sh
 # RUN: echo 'printf "End of search list.\r\n" >&2' >> %t.dir/bin/my_driver.sh
 # RUN: chmod +x %t.dir/bin/my_driver.sh
 
@@ -29,6 +31,8 @@
 # RUN: touch %t.dir/my/dir/a.h
 # RUN: mkdir -p %t.dir/my/dir2
 # RUN: touch %t.dir/my/dir2/b.h
+# RUN: mkdir -p %t.dir/builtin
+# RUN: touch %t.dir/builtin/c.h
 
 # Generate a compile_commands.json that will query the mock driver we've
 # created. Which should add a.h and b.h into include search path.
@@ -53,7 +57,7 @@
   "uri": "file://INPUT_DIR/the-file.cpp",
   "languageId":"cpp",
   "version":1,
-  "text":"#include \n#include \n#if !defined(__ARM_ARCH) || !defined(__gnu_linux__)\n#error \"Invalid target\"\n#endif\n"
+  "text":"#include \n#include \n#if !defined(__ARM_ARCH) || !defined(__gnu_linux__)\n#error \"Invalid target\"\n#endif\n#if __has_include()\n#error \"wrong-toolchain builtins\"\n#endif\n"
 }
   }
 }
Index: clang-tools-extra/clangd/SystemIncludeExtractor.cpp
===
--- clang-tools-extra/clangd/SystemIncludeExtractor.cpp
+++ clang-tools-extra/clangd/SystemIncludeExtractor.cpp
@@ -83,17 +83,16 @@
   // Whether certain includes should be part of query.
   bool StandardIncludes = true;
   bool StandardCXXIncludes = true;
-  bool BuiltinIncludes = true;
   // Language to use while querying.
   std::string Lang;
   std::string Sysroot;
   std::string ISysroot;
 
   bool operator==(const DriverArgs &RHS) const {
-return std::tie(Driver, StandardIncludes, StandardCXXIncludes,
-BuiltinIncludes, Lang, Sysroot, ISysroot) ==
+return std::tie(Driver, StandardIncludes, StandardCXXIncludes, Lang,
+Sysroot, ISysroot) ==
std::tie(RHS.Driver, RHS.StandardIncludes, RHS.StandardCXXIncludes,
-RHS.BuiltinIncludes, RHS.Lang, RHS.Sysroot, ISysroot);
+RHS.Lang, RHS.Sysroot, ISysroot);
   }
 
   DriverArgs(const tooling::CompileCommand &Cmd, llvm::StringRef File) {
@@ -120,8 +119,6 @@
 StandardIncludes = false;
   else if (Arg == "-nostdinc++")
 StandardCXXIncludes = false;
-  else if (Arg == "-nobuiltininc")
-BuiltinIncludes = false;
   // Figure out sysroot
   else if (Arg.consume_front("--sysroot")) {
 if (Arg.consume_front("="))
@@ -156,8 +153,6 @@
   Args.push_back("-nostdinc");
 if (!StandardCXXIncludes)
   Args.push_back("-nostdinc++");
-if (!BuiltinIncludes)
-  Args.push_back("-nobuiltininc++");
 if (!Sysroot.empty())
   Args.append({"--sysroot", Sysroot});
 if (!ISysroot.empty())
@@ -190,7 +185,6 @@
 Val.Driver,
 Val.StandardIncludes,
 Val.StandardCXXIncludes,
-Val.BuiltinIncludes,
 Val.Lang,
 Val.Sysroot,
 Val.ISysroot,
@@ -274,6 +268,42 @@
   return std::move(Info);
 }
 
+std::optional run(llvm::ArrayRef Argv,
+   bool OutputIsStderr) {
+  llvm::SmallString<128> OutputPath;
+  if (auto EC = llvm::sys::fs::createTemporaryFile("system-includes", "clangd",
+   OutputPath)) {
+elog("System include extraction: failed to create temporary file with "
+ "error {0}",
+ EC.message());
+return std::nullopt;
+  }
+  auto CleanUp = llvm::make_scope_exit(
+  [&OutputPath]() { llvm::sys::fs::remove(OutputPath); });
+
+  std::optional Redirects[] = {{""}, {""}, {""}};
+  Redirects[OutputIsStderr ? 2 : 1] = OutputPath.str();
+
+  std::string ErrMsg;
+  if

[PATCH] D156044: [clangd] Exclude builtin headers from system include extraction

2023-07-22 Thread Sam McCall via Phabricator via cfe-commits
sammccall updated this revision to Diff 543253.
sammccall added a comment.

remove (broken) support for -nobuiltininc


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D156044/new/

https://reviews.llvm.org/D156044

Files:
  clang-tools-extra/clangd/SystemIncludeExtractor.cpp
  clang-tools-extra/clangd/test/system-include-extractor.test

Index: clang-tools-extra/clangd/test/system-include-extractor.test
===
--- clang-tools-extra/clangd/test/system-include-extractor.test
+++ clang-tools-extra/clangd/test/system-include-extractor.test
@@ -10,6 +10,7 @@
 # %temp_dir%/my/dir2 as include search paths.
 # RUN: echo '#!/bin/sh' >> %t.dir/bin/my_driver.sh
 # RUN: echo '[ "$0" = "%t.dir/bin/my_driver.sh" ] || exit' >> %t.dir/bin/my_driver.sh
+# RUN: echo '[ "$1" = "-print-file-name=include" ] && echo "%t.dir/builtin" && exit' >> %t.dir/bin/my_driver.sh
 # RUN: echo 'args="$*"' >> %t.dir/bin/my_driver.sh
 # Check that clangd preserves certain flags like `-nostdinc` from
 # original invocation in compile_commands.json.
@@ -21,6 +22,7 @@
 # RUN: echo 'printf "#include <...> search starts here:\r\n" >&2' >> %t.dir/bin/my_driver.sh
 # RUN: echo 'echo %t.dir/my/dir/ >&2' >> %t.dir/bin/my_driver.sh
 # RUN: echo 'echo %t.dir/my/dir2/ >&2' >> %t.dir/bin/my_driver.sh
+# RUN: echo 'echo %t.dir/builtin >&2' >> %t.dir/bin/my_driver.sh
 # RUN: echo 'printf "End of search list.\r\n" >&2' >> %t.dir/bin/my_driver.sh
 # RUN: chmod +x %t.dir/bin/my_driver.sh
 
@@ -29,6 +31,8 @@
 # RUN: touch %t.dir/my/dir/a.h
 # RUN: mkdir -p %t.dir/my/dir2
 # RUN: touch %t.dir/my/dir2/b.h
+# RUN: mkdir -p %t.dir/builtin
+# RUN: touch %t.dir/builtin/c.h
 
 # Generate a compile_commands.json that will query the mock driver we've
 # created. Which should add a.h and b.h into include search path.
@@ -53,7 +57,7 @@
   "uri": "file://INPUT_DIR/the-file.cpp",
   "languageId":"cpp",
   "version":1,
-  "text":"#include \n#include \n#if !defined(__ARM_ARCH) || !defined(__gnu_linux__)\n#error \"Invalid target\"\n#endif\n"
+  "text":"#include \n#include \n#if !defined(__ARM_ARCH) || !defined(__gnu_linux__)\n#error \"Invalid target\"\n#endif\n#if __has_include()\n#error \"wrong-toolchain builtins\"\n#endif\n"
 }
   }
 }
Index: clang-tools-extra/clangd/SystemIncludeExtractor.cpp
===
--- clang-tools-extra/clangd/SystemIncludeExtractor.cpp
+++ clang-tools-extra/clangd/SystemIncludeExtractor.cpp
@@ -83,7 +83,6 @@
   // Whether certain includes should be part of query.
   bool StandardIncludes = true;
   bool StandardCXXIncludes = true;
-  bool BuiltinIncludes = true;
   // Language to use while querying.
   std::string Lang;
   std::string Sysroot;
@@ -120,8 +119,6 @@
 StandardIncludes = false;
   else if (Arg == "-nostdinc++")
 StandardCXXIncludes = false;
-  else if (Arg == "-nobuiltininc")
-BuiltinIncludes = false;
   // Figure out sysroot
   else if (Arg.consume_front("--sysroot")) {
 if (Arg.consume_front("="))
@@ -156,8 +153,6 @@
   Args.push_back("-nostdinc");
 if (!StandardCXXIncludes)
   Args.push_back("-nostdinc++");
-if (!BuiltinIncludes)
-  Args.push_back("-nobuiltininc++");
 if (!Sysroot.empty())
   Args.append({"--sysroot", Sysroot});
 if (!ISysroot.empty())
@@ -274,6 +269,42 @@
   return std::move(Info);
 }
 
+std::optional run(llvm::ArrayRef Argv,
+   bool OutputIsStderr) {
+  llvm::SmallString<128> OutputPath;
+  if (auto EC = llvm::sys::fs::createTemporaryFile("system-includes", "clangd",
+   OutputPath)) {
+elog("System include extraction: failed to create temporary file with "
+ "error {0}",
+ EC.message());
+return std::nullopt;
+  }
+  auto CleanUp = llvm::make_scope_exit(
+  [&OutputPath]() { llvm::sys::fs::remove(OutputPath); });
+
+  std::optional Redirects[] = {{""}, {""}, {""}};
+  Redirects[OutputIsStderr ? 2 : 1] = OutputPath.str();
+
+  std::string ErrMsg;
+  if (int RC =
+  llvm::sys::ExecuteAndWait(Argv.front(), Argv, /*Env=*/std::nullopt,
+Redirects, /*SecondsToWait=*/0,
+/*MemoryLimit=*/0, &ErrMsg)) {
+elog("System include extraction: driver execution failed with return code: "
+ "{0} - '{1}'. Args: [{2}]",
+ llvm::to_string(RC), ErrMsg, printArgv(Argv));
+return std::nullopt;
+  }
+
+  auto BufOrError = llvm::MemoryBuffer::getFile(OutputPath);
+  if (!BufOrError) {
+elog("System include extraction: failed to read {0} with error {1}",
+ OutputPath, BufOrError.getError().message());
+return std::nullopt;
+  }
+  return BufOrError.get().get()->getBuffer().str();
+}
+
 std::optional
 extractSystemIncludesAndTarget(const DriverArgs &InputArgs,
  

[PATCH] D156044: [clangd] Exclude builtin headers from system include extraction

2023-07-22 Thread Sam McCall via Phabricator via cfe-commits
sammccall created this revision.
sammccall added reviewers: kadircet, madscientist.
Herald added a subscriber: arphaman.
Herald added a project: All.
sammccall requested review of this revision.
Herald added subscribers: cfe-commits, MaskRay, ilya-biryukov.
Herald added a project: clang-tools-extra.

Most headers that we discover are likely to be fairly portable across at least
clang/gcc, which is why we get away with adding them to clangd's search path.

This is not the case for the compiler builtin headers, which are shipped with
the parser and rely on parser builtins/features. We're better off *not* using
the the scanned builtin headers, and hoping our own intrinsics provide the
interface the code is relying on.

Fixes https://github.com/clangd/clangd/issues/1695


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D156044

Files:
  clang-tools-extra/clangd/SystemIncludeExtractor.cpp
  clang-tools-extra/clangd/test/system-include-extractor.test

Index: clang-tools-extra/clangd/test/system-include-extractor.test
===
--- clang-tools-extra/clangd/test/system-include-extractor.test
+++ clang-tools-extra/clangd/test/system-include-extractor.test
@@ -10,6 +10,7 @@
 # %temp_dir%/my/dir2 as include search paths.
 # RUN: echo '#!/bin/sh' >> %t.dir/bin/my_driver.sh
 # RUN: echo '[ "$0" = "%t.dir/bin/my_driver.sh" ] || exit' >> %t.dir/bin/my_driver.sh
+# RUN: echo '[ "$1" = "-print-file-name=include" ] && echo "%t.dir/builtin" && exit' >> %t.dir/bin/my_driver.sh
 # RUN: echo 'args="$*"' >> %t.dir/bin/my_driver.sh
 # Check that clangd preserves certain flags like `-nostdinc` from
 # original invocation in compile_commands.json.
@@ -21,6 +22,7 @@
 # RUN: echo 'printf "#include <...> search starts here:\r\n" >&2' >> %t.dir/bin/my_driver.sh
 # RUN: echo 'echo %t.dir/my/dir/ >&2' >> %t.dir/bin/my_driver.sh
 # RUN: echo 'echo %t.dir/my/dir2/ >&2' >> %t.dir/bin/my_driver.sh
+# RUN: echo 'echo %t.dir/builtin >&2' >> %t.dir/bin/my_driver.sh
 # RUN: echo 'printf "End of search list.\r\n" >&2' >> %t.dir/bin/my_driver.sh
 # RUN: chmod +x %t.dir/bin/my_driver.sh
 
@@ -29,6 +31,8 @@
 # RUN: touch %t.dir/my/dir/a.h
 # RUN: mkdir -p %t.dir/my/dir2
 # RUN: touch %t.dir/my/dir2/b.h
+# RUN: mkdir -p %t.dir/builtin
+# RUN: touch %t.dir/builtin/c.h
 
 # Generate a compile_commands.json that will query the mock driver we've
 # created. Which should add a.h and b.h into include search path.
@@ -53,7 +57,7 @@
   "uri": "file://INPUT_DIR/the-file.cpp",
   "languageId":"cpp",
   "version":1,
-  "text":"#include \n#include \n#if !defined(__ARM_ARCH) || !defined(__gnu_linux__)\n#error \"Invalid target\"\n#endif\n"
+  "text":"#include \n#include \n#if !defined(__ARM_ARCH) || !defined(__gnu_linux__)\n#error \"Invalid target\"\n#endif\n#if __has_include()\n#error \"wrong-toolchain builtins\"\n#endif\n"
 }
   }
 }
Index: clang-tools-extra/clangd/SystemIncludeExtractor.cpp
===
--- clang-tools-extra/clangd/SystemIncludeExtractor.cpp
+++ clang-tools-extra/clangd/SystemIncludeExtractor.cpp
@@ -157,7 +157,7 @@
 if (!StandardCXXIncludes)
   Args.push_back("-nostdinc++");
 if (!BuiltinIncludes)
-  Args.push_back("-nobuiltininc++");
+  Args.push_back("-nobuiltininc");
 if (!Sysroot.empty())
   Args.append({"--sysroot", Sysroot});
 if (!ISysroot.empty())
@@ -274,6 +274,42 @@
   return std::move(Info);
 }
 
+std::optional run(llvm::ArrayRef Argv,
+   bool OutputIsStderr) {
+  llvm::SmallString<128> OutputPath;
+  if (auto EC = llvm::sys::fs::createTemporaryFile("system-includes", "clangd",
+   OutputPath)) {
+elog("System include extraction: failed to create temporary file with "
+ "error {0}",
+ EC.message());
+return std::nullopt;
+  }
+  auto CleanUp = llvm::make_scope_exit(
+  [&OutputPath]() { llvm::sys::fs::remove(OutputPath); });
+
+  std::optional Redirects[] = {{""}, {""}, {""}};
+  Redirects[OutputIsStderr ? 2 : 1] = OutputPath.str();
+
+  std::string ErrMsg;
+  if (int RC =
+  llvm::sys::ExecuteAndWait(Argv.front(), Argv, /*Env=*/std::nullopt,
+Redirects, /*SecondsToWait=*/0,
+/*MemoryLimit=*/0, &ErrMsg)) {
+elog("System include extraction: driver execution failed with return code: "
+ "{0} - '{1}'. Args: [{2}]",
+ llvm::to_string(RC), ErrMsg, printArgv(Argv));
+return std::nullopt;
+  }
+
+  auto BufOrError = llvm::MemoryBuffer::getFile(OutputPath);
+  if (!BufOrError) {
+elog("System include extraction: failed to read {0} with error {1}",
+ OutputPath, BufOrError.getError().message());
+return std::nullopt;
+  }
+  return BufOrError.get().get()->getBuffer().str();
+}
+
 std::optional
 extractSystemIncludesAndTarget(const DriverAr