[clang] [Clang] Take libstdc++ into account during GCC detection (PR #145056)

Frederik Harwath via cfe-commits cfe-commits at lists.llvm.org
Fri Jun 20 08:45:30 PDT 2025


https://github.com/frederik-h created https://github.com/llvm/llvm-project/pull/145056

The Generic_GCC::GCCInstallationDetector class always picks the latest available GCC installation directory. This often breaks C++ compilation on systems on which this directory does not contain a libstdc++ installation. On Ubuntu 22.04 systems, for instance, this can happen if the "gcc-12" package gets installed without the corresponding "g++-12" or "libstdc++-12" package in addition to the default "g++-11" installation.

This patch changes the GCC installation selection, if compiling for C++ using libstdc++, to consider only GCC installation directories which also contain libstdc++. This is accomplished by enabling the GCCInstallationDetector to reuse the existing functionality for determinig the libstdc++ include directories which had to be decoupled from its existing uses.

>From 87c03ace23467a7d6cb7e466a02309b5b287a013 Mon Sep 17 00:00:00 2001
From: Frederik Harwath <fharwath at amd.com>
Date: Wed, 19 Feb 2025 16:01:56 +0100
Subject: [PATCH] [Clang] Take libstdc++ into account during GCC detection

The Generic_GCC::GCCInstallationDetector class always picks the latest
available GCC installation directory. This often breaks C++
compilation on systems on which this directory does not contain a
libstdc++ installation. On Ubuntu 22.04 systems, for instance, this
can happen if the "gcc-12" package gets installed without the
corresponding "g++-12" or "libstdc++-12" package in addition to the
default "g++-11" installation.

This patch changes the GCC installation selection, if compiling for
C++ using libstdc++, to consider only GCC installation directories
which also contain libstdc++. This is accomplished by enabling the
GCCInstallationDetector to reuse the existing functionality for
determinig the libstdc++ include directories which had to be
decoupled from its existing uses.
---
 clang/include/clang/Driver/Options.td         |   5 +-
 clang/include/clang/Driver/ToolChain.h        |  27 ++-
 clang/lib/Driver/ToolChain.cpp                |  25 ++-
 clang/lib/Driver/ToolChains/AVR.cpp           |   3 +-
 clang/lib/Driver/ToolChains/CSKYToolChain.cpp |   2 +-
 clang/lib/Driver/ToolChains/Gnu.cpp           | 191 ++++++++++++------
 clang/lib/Driver/ToolChains/Gnu.h             |  84 ++++++--
 clang/lib/Driver/ToolChains/Haiku.cpp         |   2 +-
 clang/lib/Driver/ToolChains/Hurd.cpp          |   2 +-
 clang/lib/Driver/ToolChains/Linux.cpp         |   2 +-
 clang/lib/Driver/ToolChains/MSP430.cpp        |   2 +-
 .../lib/Driver/ToolChains/RISCVToolchain.cpp  |   2 +-
 clang/lib/Driver/ToolChains/Solaris.cpp       |   2 +-
 .../gcc-11.2.0/include/c++/11.2.0/.keep       |   0
 .../gcc-12/include/c++/12/.keep               |   0
 clang/test/Driver/gcc-toolchain.cpp           |   5 -
 16 files changed, 245 insertions(+), 109 deletions(-)
 create mode 100644 clang/test/Driver/Inputs/powerpc64le-linux-gnu-tree/gcc-11.2.0/include/c++/11.2.0/.keep
 create mode 100644 clang/test/Driver/Inputs/powerpc64le-linux-gnu-tree/gcc-12/include/c++/12/.keep

diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 6eabd9f76a792..65a63a990d8e3 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -732,8 +732,9 @@ def gcc_install_dir_EQ : Joined<["--"], "gcc-install-dir=">,
 def gcc_toolchain : Joined<["--"], "gcc-toolchain=">, Flags<[NoXarchOption]>,
   Visibility<[ClangOption, FlangOption]>,
   HelpText<
-    "Specify a directory where Clang can find 'include' and 'lib{,32,64}/gcc{,-cross}/$triple/$version'. "
-    "Clang will use the GCC installation with the largest version">,
+    "Specify a directory to search for GCC installations (see --gcc-install-dir). "
+    "Picks the subdirectory with the largest GCC version, "
+    "skipping those without libstdc++ if the compile flags require it.">,
   HelpTextForVariants<[FlangOption],
     "Specify a directory where Flang can find 'lib{,32,64}/gcc{,-cross}/$triple/$version'. "
     "Flang will use the GCC installation with the largest version">;
diff --git a/clang/include/clang/Driver/ToolChain.h b/clang/include/clang/Driver/ToolChain.h
index 7d1d8feebf35e..fe4e22caca5f5 100644
--- a/clang/include/clang/Driver/ToolChain.h
+++ b/clang/include/clang/Driver/ToolChain.h
@@ -226,25 +226,32 @@ class ToolChain {
 
   /// \name Utilities for implementing subclasses.
   ///@{
-  static void addSystemInclude(const llvm::opt::ArgList &DriverArgs,
-                               llvm::opt::ArgStringList &CC1Args,
-                               const Twine &Path);
   static void addExternCSystemInclude(const llvm::opt::ArgList &DriverArgs,
                                       llvm::opt::ArgStringList &CC1Args,
                                       const Twine &Path);
   static void
-      addExternCSystemIncludeIfExists(const llvm::opt::ArgList &DriverArgs,
-                                      llvm::opt::ArgStringList &CC1Args,
-                                      const Twine &Path);
-  static void addSystemIncludes(const llvm::opt::ArgList &DriverArgs,
-                                llvm::opt::ArgStringList &CC1Args,
-                                ArrayRef<StringRef> Paths);
-
+  addExternCSystemIncludeIfExists(const llvm::opt::ArgList &DriverArgs,
+                                  llvm::opt::ArgStringList &CC1Args,
+                                  const Twine &Path);
   static std::string concat(StringRef Path, const Twine &A, const Twine &B = "",
                             const Twine &C = "", const Twine &D = "");
+
+  /// Return the CXXStdlibType which has been selected based on the
+  /// type of ToolChain driver args, triple etc. if it is in use,
+  /// i.e. we are compiling for C++ and the stdlib has not been
+  /// disabled.
+  std::optional<CXXStdlibType> getCXXStdlibTypeInUse();
+
   ///@}
 
 public:
+  static void addSystemInclude(const llvm::opt::ArgList &DriverArgs,
+                               llvm::opt::ArgStringList &CC1Args,
+                               const Twine &Path);
+  static void addSystemIncludes(const llvm::opt::ArgList &DriverArgs,
+                                llvm::opt::ArgStringList &CC1Args,
+                                ArrayRef<StringRef> Paths);
+
   virtual ~ToolChain();
 
   // Accessors
diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp
index ebc982096595e..58f5034ccd19f 100644
--- a/clang/lib/Driver/ToolChain.cpp
+++ b/clang/lib/Driver/ToolChain.cpp
@@ -104,6 +104,15 @@ ToolChain::ToolChain(const Driver &D, const llvm::Triple &T,
     addIfExists(getFilePaths(), Path);
 }
 
+std::optional<ToolChain::CXXStdlibType> ToolChain::getCXXStdlibTypeInUse() {
+  if (!(D.CCCIsCXX() &&
+        !Args.hasArg(options::OPT_nostdinc, options::OPT_nostdincxx,
+                     options::OPT_nostdlibinc)))
+    return std::nullopt;
+
+  return GetCXXStdlibType(Args);
+}
+
 llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>>
 ToolChain::executeToolChainProgram(StringRef Executable) const {
   llvm::SmallString<64> OutputFile;
@@ -1272,14 +1281,6 @@ ToolChain::CXXStdlibType ToolChain::GetCXXStdlibType(const ArgList &Args) const{
   return *cxxStdlibType;
 }
 
-/// Utility function to add a system include directory to CC1 arguments.
-/*static*/ void ToolChain::addSystemInclude(const ArgList &DriverArgs,
-                                            ArgStringList &CC1Args,
-                                            const Twine &Path) {
-  CC1Args.push_back("-internal-isystem");
-  CC1Args.push_back(DriverArgs.MakeArgString(Path));
-}
-
 /// Utility function to add a system include directory with extern "C"
 /// semantics to CC1 arguments.
 ///
@@ -1302,6 +1303,14 @@ void ToolChain::addExternCSystemIncludeIfExists(const ArgList &DriverArgs,
     addExternCSystemInclude(DriverArgs, CC1Args, Path);
 }
 
+/// Utility function to add a system include directory to CC1 arguments.
+/*static*/ void ToolChain::addSystemInclude(const ArgList &DriverArgs,
+                                            ArgStringList &CC1Args,
+                                            const Twine &Path) {
+  CC1Args.push_back("-internal-isystem");
+  CC1Args.push_back(DriverArgs.MakeArgString(Path));
+}
+
 /// Utility function to add a list of system include directories to CC1.
 /*static*/ void ToolChain::addSystemIncludes(const ArgList &DriverArgs,
                                              ArgStringList &CC1Args,
diff --git a/clang/lib/Driver/ToolChains/AVR.cpp b/clang/lib/Driver/ToolChains/AVR.cpp
index 08e906ac9e806..ef5806c4231b1 100644
--- a/clang/lib/Driver/ToolChains/AVR.cpp
+++ b/clang/lib/Driver/ToolChains/AVR.cpp
@@ -366,7 +366,8 @@ const StringRef PossibleAVRLibcLocations[] = {
 AVRToolChain::AVRToolChain(const Driver &D, const llvm::Triple &Triple,
                            const ArgList &Args)
     : Generic_ELF(D, Triple, Args) {
-  GCCInstallation.init(Triple, Args);
+
+  GCCInstallation.init(Triple, Args, getCXXStdlibTypeInUse());
 
   if (getCPUName(D, Args, Triple).empty())
     D.Diag(diag::warn_drv_avr_mcu_not_specified);
diff --git a/clang/lib/Driver/ToolChains/CSKYToolChain.cpp b/clang/lib/Driver/ToolChains/CSKYToolChain.cpp
index feb3bc922920f..c3f4dcf3ed342 100644
--- a/clang/lib/Driver/ToolChains/CSKYToolChain.cpp
+++ b/clang/lib/Driver/ToolChains/CSKYToolChain.cpp
@@ -35,7 +35,7 @@ static void addMultilibsFilePaths(const Driver &D, const MultilibSet &Multilibs,
 CSKYToolChain::CSKYToolChain(const Driver &D, const llvm::Triple &Triple,
                              const ArgList &Args)
     : Generic_ELF(D, Triple, Args) {
-  GCCInstallation.init(Triple, Args);
+  GCCInstallation.init(Triple, Args, getCXXStdlibTypeInUse());
   if (GCCInstallation.isValid()) {
     Multilibs = GCCInstallation.getMultilibs();
     SelectedMultilibs.assign({GCCInstallation.getMultilib()});
diff --git a/clang/lib/Driver/ToolChains/Gnu.cpp b/clang/lib/Driver/ToolChains/Gnu.cpp
index f56eeda3cb5f6..5fdd690d01316 100644
--- a/clang/lib/Driver/ToolChains/Gnu.cpp
+++ b/clang/lib/Driver/ToolChains/Gnu.cpp
@@ -34,6 +34,7 @@
 #include "llvm/TargetParser/RISCVISAInfo.h"
 #include "llvm/TargetParser/TargetParser.h"
 #include <system_error>
+#include <algorithm>
 
 using namespace clang::driver;
 using namespace clang::driver::toolchains;
@@ -2171,7 +2172,10 @@ static llvm::StringRef getGCCToolchainDir(const ArgList &Args,
 /// necessary because the driver doesn't store the final version of the target
 /// triple.
 void Generic_GCC::GCCInstallationDetector::init(
-    const llvm::Triple &TargetTriple, const ArgList &Args) {
+    const llvm::Triple &TargetTriple, const ArgList &Args,
+    std::optional<ToolChain::CXXStdlibType> CxxLibType) {
+  RequireLibStdCxx = CxxLibType && CxxLibType == CXXStdlibType::CST_Libstdcxx;
+
   llvm::Triple BiarchVariantTriple = TargetTriple.isArch32Bit()
                                          ? TargetTriple.get64BitArchVariant()
                                          : TargetTriple.get32BitArchVariant();
@@ -2213,10 +2217,11 @@ void Generic_GCC::GCCInstallationDetector::init(
       StringRef TripleText =
           llvm::sys::path::filename(llvm::sys::path::parent_path(InstallDir));
 
-      Version = GCCVersion::Parse(VersionText);
-      GCCTriple.setTriple(TripleText);
-      GCCInstallPath = std::string(InstallDir);
-      GCCParentLibPath = GCCInstallPath + "/../../..";
+      SelectedInstallation.Version = GCCVersion::Parse(VersionText);
+      SelectedInstallation.GCCTriple.setTriple(TripleText);
+      SelectedInstallation.GCCInstallPath = std::string(InstallDir);
+      SelectedInstallation.GCCParentLibPath =
+          SelectedInstallation.GCCInstallPath + "/../../..";
       IsValid = true;
     }
     return;
@@ -2276,7 +2281,7 @@ void Generic_GCC::GCCInstallationDetector::init(
   // Loop over the various components which exist and select the best GCC
   // installation available. GCC installs are ranked by version number.
   const GCCVersion VersionZero = GCCVersion::Parse("0.0.0");
-  Version = VersionZero;
+  SelectedInstallation.Version = VersionZero;
   for (const std::string &Prefix : Prefixes) {
     auto &VFS = D.getVFS();
     if (!VFS.exists(Prefix))
@@ -2304,7 +2309,7 @@ void Generic_GCC::GCCInstallationDetector::init(
     }
 
     // Skip other prefixes once a GCC installation is found.
-    if (Version > VersionZero)
+    if (SelectedInstallation.Version > VersionZero)
       break;
   }
 }
@@ -2313,14 +2318,17 @@ void Generic_GCC::GCCInstallationDetector::print(raw_ostream &OS) const {
   for (const auto &InstallPath : CandidateGCCInstallPaths)
     OS << "Found candidate GCC installation: " << InstallPath << "\n";
 
-  if (!GCCInstallPath.empty())
-    OS << "Selected GCC installation: " << GCCInstallPath << "\n";
+  if (!SelectedInstallation.GCCInstallPath.empty())
+    OS << "Selected GCC installation: " << SelectedInstallation.GCCInstallPath
+       << "\n";
 
   for (const auto &Multilib : Multilibs)
     OS << "Candidate multilib: " << Multilib << "\n";
 
-  if (Multilibs.size() != 0 || !SelectedMultilib.isDefault())
-    OS << "Selected multilib: " << SelectedMultilib << "\n";
+  if (Multilibs.size() != 0 ||
+      !SelectedInstallation.SelectedMultilib.isDefault())
+    OS << "Selected multilib: " << SelectedInstallation.SelectedMultilib
+       << "\n";
 }
 
 bool Generic_GCC::GCCInstallationDetector::getBiarchSibling(Multilib &M) const {
@@ -2842,9 +2850,9 @@ bool Generic_GCC::GCCInstallationDetector::ScanGCCForMultilibs(
   }
 
   Multilibs = Detected.Multilibs;
-  SelectedMultilib = Detected.SelectedMultilibs.empty()
-                         ? Multilib()
-                         : Detected.SelectedMultilibs.back();
+  SelectedInstallation.SelectedMultilib =
+      Detected.SelectedMultilibs.empty() ? Multilib()
+                                         : Detected.SelectedMultilibs.back();
   BiarchSibling = Detected.BiarchSibling;
 
   return true;
@@ -2883,6 +2891,7 @@ void Generic_GCC::GCCInstallationDetector::ScanLibDirForGCCTriple(
     if (!Suffix.Active)
       continue;
 
+    SmallVector<GCCInstallCandidate, 3> Installations;
     StringRef LibSuffix = Suffix.LibSuffix;
     std::error_code EC;
     for (llvm::vfs::directory_iterator
@@ -2896,20 +2905,40 @@ void Generic_GCC::GCCInstallationDetector::ScanLibDirForGCCTriple(
           continue; // Saw this path before; no need to look at it again.
       if (CandidateVersion.isOlderThan(4, 1, 1))
         continue;
-      if (CandidateVersion <= Version)
+      if (CandidateVersion <= SelectedInstallation.Version && IsValid)
         continue;
 
       if (!ScanGCCForMultilibs(TargetTriple, Args, LI->path(),
                                NeedsBiarchSuffix))
         continue;
 
-      Version = CandidateVersion;
-      GCCTriple.setTriple(CandidateTriple);
+      GCCInstallCandidate Installation;
+      Installation.Version = CandidateVersion;
+      Installation.GCCTriple.setTriple(CandidateTriple);
       // FIXME: We hack together the directory name here instead of
       // using LI to ensure stable path separators across Windows and
       // Linux.
-      GCCInstallPath = (LibDir + "/" + LibSuffix + "/" + VersionText).str();
-      GCCParentLibPath = (GCCInstallPath + "/../" + Suffix.ReversePath).str();
+      Installation.GCCInstallPath =
+          (LibDir + "/" + LibSuffix + "/" + VersionText).str();
+      Installation.GCCParentLibPath =
+          (Installation.GCCInstallPath + "/../" + Suffix.ReversePath).str();
+      Installation.SelectedMultilib = getMultilib();
+
+      Installations.push_back(Installation);
+      if (GCCInstallationHasRequiredLibs(Installation, Args)) {
+        SelectedInstallation = Installation;
+        IsValid = true;
+      }
+    }
+
+    // If no GCC installation has all required libs, pick the latest
+    // one. Otherwise we would, for instance, break C++ compilation
+    // for code which does not use anything from the stdlib but
+    // which gets compiled without "-nostdlib".
+    if (!IsValid && !Installations.empty()) {
+      SelectedInstallation = *std::max_element(
+          Installations.begin(), Installations.end(),
+          [](auto x, auto y) { return x.Version < y.Version; });
       IsValid = true;
     }
   }
@@ -2990,10 +3019,12 @@ bool Generic_GCC::GCCInstallationDetector::ScanGentooGccConfig(
                                    NeedsBiarchSuffix))
             continue;
 
-          Version = GCCVersion::Parse(ActiveVersion.second);
-          GCCInstallPath = GentooPath;
-          GCCParentLibPath = GentooPath + std::string("/../../..");
-          GCCTriple.setTriple(ActiveVersion.first);
+          SelectedInstallation.Version =
+              GCCVersion::Parse(ActiveVersion.second);
+          SelectedInstallation.GCCInstallPath = GentooPath;
+          SelectedInstallation.GCCParentLibPath =
+              GentooPath + std::string("/../../..");
+          SelectedInstallation.GCCTriple.setTriple(ActiveVersion.first);
           IsValid = true;
           return true;
         }
@@ -3196,8 +3227,9 @@ void Generic_GCC::AddMultilibIncludeArgs(const ArgList &DriverArgs,
   // gcc TOOL_INCLUDE_DIR.
   const llvm::Triple &GCCTriple = GCCInstallation.getTriple();
   std::string LibPath(GCCInstallation.getParentLibPath());
-  addSystemInclude(DriverArgs, CC1Args,
-                   Twine(LibPath) + "/../" + GCCTriple.str() + "/include");
+  ToolChain::addSystemInclude(DriverArgs, CC1Args,
+                              Twine(LibPath) + "/../" + GCCTriple.str() +
+                                  "/include");
 
   const auto &Callback = Multilibs.includeDirsCallback();
   if (Callback) {
@@ -3284,12 +3316,14 @@ Generic_GCC::addLibCxxIncludePaths(const llvm::opt::ArgList &DriverArgs,
     return;
 }
 
-bool Generic_GCC::addLibStdCXXIncludePaths(Twine IncludeDir, StringRef Triple,
-                                           Twine IncludeSuffix,
-                                           const llvm::opt::ArgList &DriverArgs,
-                                           llvm::opt::ArgStringList &CC1Args,
-                                           bool DetectDebian) const {
-  if (!getVFS().exists(IncludeDir))
+static bool addLibStdCXXIncludePaths(llvm::vfs::FileSystem &vfs,
+                                     Twine IncludeDir, StringRef Triple,
+                                     Twine IncludeSuffix,
+                                     const llvm::opt::ArgList &DriverArgs,
+                                     llvm::opt::ArgStringList &CC1Args,
+                                     bool DetectDebian = false) {
+
+  if (!vfs.exists(IncludeDir))
     return false;
 
   // Debian native gcc uses g++-multiarch-incdir.diff which uses
@@ -3301,39 +3335,48 @@ bool Generic_GCC::addLibStdCXXIncludePaths(Twine IncludeDir, StringRef Triple,
   std::string Path =
       (Include + "/" + Triple + Dir.substr(Include.size()) + IncludeSuffix)
           .str();
-  if (DetectDebian && !getVFS().exists(Path))
+  if (DetectDebian && !vfs.exists(Path))
     return false;
 
   // GPLUSPLUS_INCLUDE_DIR
-  addSystemInclude(DriverArgs, CC1Args, IncludeDir);
+  ToolChain::addSystemInclude(DriverArgs, CC1Args, IncludeDir);
   // GPLUSPLUS_TOOL_INCLUDE_DIR. If Triple is not empty, add a target-dependent
   // include directory.
   if (DetectDebian)
-    addSystemInclude(DriverArgs, CC1Args, Path);
+    ToolChain::addSystemInclude(DriverArgs, CC1Args, Path);
   else if (!Triple.empty())
-    addSystemInclude(DriverArgs, CC1Args,
-                     IncludeDir + "/" + Triple + IncludeSuffix);
+    ToolChain::addSystemInclude(DriverArgs, CC1Args,
+                                IncludeDir + "/" + Triple + IncludeSuffix);
   // GPLUSPLUS_BACKWARD_INCLUDE_DIR
-  addSystemInclude(DriverArgs, CC1Args, IncludeDir + "/backward");
+  ToolChain::addSystemInclude(DriverArgs, CC1Args, IncludeDir + "/backward");
   return true;
 }
 
-bool Generic_GCC::addGCCLibStdCxxIncludePaths(
-    const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args,
-    StringRef DebianMultiarch) const {
-  assert(GCCInstallation.isValid());
+bool Generic_GCC::addLibStdCXXIncludePaths(Twine IncludeDir, StringRef Triple,
+                                           Twine IncludeSuffix,
+                                           const llvm::opt::ArgList &DriverArgs,
+                                           llvm::opt::ArgStringList &CC1Args,
+                                           bool DetectDebian) const {
+  return ::addLibStdCXXIncludePaths(getVFS(), IncludeDir, Triple, IncludeSuffix,
+                                    DriverArgs, CC1Args, DetectDebian);
+}
+
+bool Generic_GCC::GCCInstallCandidate::addGCCLibStdCxxIncludePaths(
+    llvm::vfs::FileSystem &vfs, const llvm::opt::ArgList &DriverArgs,
+    llvm::opt::ArgStringList &CC1Args, StringRef DebianMultiarch) const {
 
   // By default, look for the C++ headers in an include directory adjacent to
   // the lib directory of the GCC installation. Note that this is expect to be
   // equivalent to '/usr/include/c++/X.Y' in almost all cases.
-  StringRef LibDir = GCCInstallation.getParentLibPath();
-  StringRef InstallDir = GCCInstallation.getInstallPath();
-  StringRef TripleStr = GCCInstallation.getTriple().str();
-  const Multilib &Multilib = GCCInstallation.getMultilib();
-  const GCCVersion &Version = GCCInstallation.getVersion();
+  StringRef LibDir = getParentLibPath();
+  StringRef InstallDir = getInstallPath();
+  StringRef TripleStr = getTriple().str();
+  const Multilib &Multilib = getMultilib();
+  const GCCVersion &Version = getVersion();
 
   // Try /../$triple/include/c++/$version (gcc --print-multiarch is not empty).
-  if (addLibStdCXXIncludePaths(
+  if (::addLibStdCXXIncludePaths(
+          vfs,
           LibDir.str() + "/../" + TripleStr + "/include/c++/" + Version.Text,
           TripleStr, Multilib.includeSuffix(), DriverArgs, CC1Args))
     return true;
@@ -3341,22 +3384,24 @@ bool Generic_GCC::addGCCLibStdCxxIncludePaths(
   // Try /gcc/$triple/$version/include/c++/ (gcc --print-multiarch is not
   // empty). Like above but for GCC built with
   // --enable-version-specific-runtime-libs.
-  if (addLibStdCXXIncludePaths(LibDir.str() + "/gcc/" + TripleStr + "/" +
-                                   Version.Text + "/include/c++/",
-                               TripleStr, Multilib.includeSuffix(), DriverArgs,
-                               CC1Args))
+  if (::addLibStdCXXIncludePaths(vfs,
+                                 LibDir.str() + "/gcc/" + TripleStr + "/" +
+                                     Version.Text + "/include/c++/",
+                                 TripleStr, Multilib.includeSuffix(),
+                                 DriverArgs, CC1Args))
     return true;
 
   // Detect Debian g++-multiarch-incdir.diff.
-  if (addLibStdCXXIncludePaths(LibDir.str() + "/../include/c++/" + Version.Text,
-                               DebianMultiarch, Multilib.includeSuffix(),
-                               DriverArgs, CC1Args, /*Debian=*/true))
+  if (::addLibStdCXXIncludePaths(
+          vfs, LibDir.str() + "/../include/c++/" + Version.Text,
+          DebianMultiarch, Multilib.includeSuffix(), DriverArgs, CC1Args,
+          /*Debian=*/true))
     return true;
 
   // Try /../include/c++/$version (gcc --print-multiarch is empty).
-  if (addLibStdCXXIncludePaths(LibDir.str() + "/../include/c++/" + Version.Text,
-                               TripleStr, Multilib.includeSuffix(), DriverArgs,
-                               CC1Args))
+  if (::addLibStdCXXIncludePaths(
+          vfs, LibDir.str() + "/../include/c++/" + Version.Text, TripleStr,
+          Multilib.includeSuffix(), DriverArgs, CC1Args))
     return true;
 
   // Otherwise, fall back on a bunch of options which don't use multiarch
@@ -3371,13 +3416,41 @@ bool Generic_GCC::addGCCLibStdCxxIncludePaths(
   };
 
   for (const auto &IncludePath : LibStdCXXIncludePathCandidates) {
-    if (addLibStdCXXIncludePaths(IncludePath, TripleStr,
-                                 Multilib.includeSuffix(), DriverArgs, CC1Args))
+    if (::addLibStdCXXIncludePaths(vfs, IncludePath, TripleStr,
+                                   Multilib.includeSuffix(), DriverArgs,
+                                   CC1Args))
       return true;
   }
   return false;
 }
 
+bool Generic_GCC::GCCInstallationDetector::GCCInstallationHasRequiredLibs(
+    const GCCInstallCandidate &GCCInstallation,
+    const llvm::opt::ArgList &DriverArgs) const {
+
+  if (!RequireLibStdCxx)
+    return true;
+
+  // The following function is meant to add the libstdc++ include
+  // paths to the CC1 argument list. Here we just want to know if this
+  // would succeed and hence we do not pass it the real arguments.
+  llvm::opt::ArgStringList dummyCC1Args;
+  StringRef TripleStr = GCCInstallation.getTriple().str();
+  StringRef DebianMultiarch =
+      getTriple().getArch() == llvm::Triple::x86 ? "i386-linux-gnu" : TripleStr;
+
+  bool found = GCCInstallation.addGCCLibStdCxxIncludePaths(
+      D.getVFS(), DriverArgs, dummyCC1Args, DebianMultiarch);
+  return found;
+}
+
+bool Generic_GCC::addGCCLibStdCxxIncludePaths(
+    const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args,
+    StringRef DebianMultiarch) const {
+  return GCCInstallation.getSelectedInstallation().addGCCLibStdCxxIncludePaths(
+      getVFS(), DriverArgs, CC1Args, DebianMultiarch);
+}
+
 void
 Generic_GCC::addLibStdCxxIncludePaths(const llvm::opt::ArgList &DriverArgs,
                                       llvm::opt::ArgStringList &CC1Args) const {
diff --git a/clang/lib/Driver/ToolChains/Gnu.h b/clang/lib/Driver/ToolChains/Gnu.h
index 3b8df71bbf9d3..faee6bd9758d6 100644
--- a/clang/lib/Driver/ToolChains/Gnu.h
+++ b/clang/lib/Driver/ToolChains/Gnu.h
@@ -184,6 +184,38 @@ class LLVM_LIBRARY_VISIBILITY Generic_GCC : public ToolChain {
     bool operator>=(const GCCVersion &RHS) const { return !(*this < RHS); }
   };
 
+  struct GCCInstallCandidate {
+    std::string GCCInstallPath;
+    std::string GCCParentLibPath;
+
+    llvm::Triple GCCTriple;
+
+    /// The primary multilib appropriate for the given flags.
+    Multilib SelectedMultilib;
+
+    GCCVersion Version;
+
+    /// Get the GCC triple for the detected install.
+    const llvm::Triple &getTriple() const { return GCCTriple; }
+
+    /// Get the detected GCC installation path.
+    StringRef getInstallPath() const { return GCCInstallPath; }
+
+    /// Get the detected GCC parent lib path.
+    StringRef getParentLibPath() const { return GCCParentLibPath; }
+
+    /// Get the detected Multilib
+    const Multilib &getMultilib() const { return SelectedMultilib; }
+
+    /// Get the detected GCC version string.
+    const GCCVersion &getVersion() const { return Version; }
+
+    bool addGCCLibStdCxxIncludePaths(llvm::vfs::FileSystem &vfs,
+                                     const llvm::opt::ArgList &DriverArgs,
+                                     llvm::opt::ArgStringList &CC1Args,
+                                     StringRef DebianMultiarch) const;
+  };
+
   /// This is a class to find a viable GCC installation for Clang to
   /// use.
   ///
@@ -192,21 +224,15 @@ class LLVM_LIBRARY_VISIBILITY Generic_GCC : public ToolChain {
   /// Driver, and has logic for fuzzing that where appropriate.
   class GCCInstallationDetector {
     bool IsValid;
-    llvm::Triple GCCTriple;
+
     const Driver &D;
 
-    // FIXME: These might be better as path objects.
-    std::string GCCInstallPath;
-    std::string GCCParentLibPath;
+    GCCInstallCandidate SelectedInstallation;
 
-    /// The primary multilib appropriate for the given flags.
-    Multilib SelectedMultilib;
     /// On Biarch systems, this corresponds to the default multilib when
     /// targeting the non-default multilib. Otherwise, it is empty.
     std::optional<Multilib> BiarchSibling;
 
-    GCCVersion Version;
-
     // We retain the list of install paths that were considered and rejected in
     // order to print out detailed information in verbose mode.
     std::set<std::string> CandidateGCCInstallPaths;
@@ -218,23 +244,40 @@ class LLVM_LIBRARY_VISIBILITY Generic_GCC : public ToolChain {
     const std::string GentooConfigDir = "/etc/env.d/gcc";
 
   public:
-    explicit GCCInstallationDetector(const Driver &D) : IsValid(false), D(D) {}
-    void init(const llvm::Triple &TargetTriple, const llvm::opt::ArgList &Args);
+    bool RequireLibStdCxx;
+
+    explicit GCCInstallationDetector(const Driver &D)
+        : IsValid(false), D(D), RequireLibStdCxx(false) {}
+
+    void init(const llvm::Triple &TargetTriple, const llvm::opt::ArgList &Args,
+              std::optional<ToolChain::CXXStdlibType> CxxLibType);
 
     /// Check whether we detected a valid GCC install.
     bool isValid() const { return IsValid; }
 
+    const GCCInstallCandidate &getSelectedInstallation() const {
+      return SelectedInstallation;
+    }
+
     /// Get the GCC triple for the detected install.
-    const llvm::Triple &getTriple() const { return GCCTriple; }
+    const llvm::Triple &getTriple() const {
+      return SelectedInstallation.GCCTriple;
+    }
 
     /// Get the detected GCC installation path.
-    StringRef getInstallPath() const { return GCCInstallPath; }
+    StringRef getInstallPath() const {
+      return SelectedInstallation.GCCInstallPath;
+    }
 
     /// Get the detected GCC parent lib path.
-    StringRef getParentLibPath() const { return GCCParentLibPath; }
+    StringRef getParentLibPath() const {
+      return SelectedInstallation.GCCParentLibPath;
+    }
 
     /// Get the detected Multilib
-    const Multilib &getMultilib() const { return SelectedMultilib; }
+    const Multilib &getMultilib() const {
+      return SelectedInstallation.SelectedMultilib;
+    }
 
     /// Get the whole MultilibSet
     const MultilibSet &getMultilibs() const { return Multilibs; }
@@ -244,7 +287,9 @@ class LLVM_LIBRARY_VISIBILITY Generic_GCC : public ToolChain {
     bool getBiarchSibling(Multilib &M) const;
 
     /// Get the detected GCC version string.
-    const GCCVersion &getVersion() const { return Version; }
+    const GCCVersion &getVersion() const {
+      return SelectedInstallation.Version;
+    }
 
     /// Print information about the detected GCC installation.
     void print(raw_ostream &OS) const;
@@ -262,9 +307,14 @@ class LLVM_LIBRARY_VISIBILITY Generic_GCC : public ToolChain {
                                SmallVectorImpl<std::string> &Prefixes,
                                StringRef SysRoot);
 
+    /// Checks if the \p GCCInstallation has the libs required
+    /// by the \p DriverArgs.
+    bool
+    GCCInstallationHasRequiredLibs(const GCCInstallCandidate &GCCInstallation,
+                                   const llvm::opt::ArgList &DriverArgs) const;
+
     bool ScanGCCForMultilibs(const llvm::Triple &TargetTriple,
-                             const llvm::opt::ArgList &Args,
-                             StringRef Path,
+                             const llvm::opt::ArgList &Args, StringRef Path,
                              bool NeedsBiarchSuffix = false);
 
     void ScanLibDirForGCCTriple(const llvm::Triple &TargetArch,
diff --git a/clang/lib/Driver/ToolChains/Haiku.cpp b/clang/lib/Driver/ToolChains/Haiku.cpp
index af74f43e48364..6c55a2f74724f 100644
--- a/clang/lib/Driver/ToolChains/Haiku.cpp
+++ b/clang/lib/Driver/ToolChains/Haiku.cpp
@@ -166,7 +166,7 @@ void haiku::Linker::ConstructJob(Compilation &C, const JobAction &JA,
 Haiku::Haiku(const Driver &D, const llvm::Triple& Triple, const ArgList &Args)
   : Generic_ELF(D, Triple, Args) {
 
-  GCCInstallation.init(Triple, Args);
+  GCCInstallation.init(Triple, Args, getCXXStdlibTypeInUse());
 
   getFilePaths().push_back(concat(getDriver().SysRoot, "/boot/system/lib"));
   getFilePaths().push_back(concat(getDriver().SysRoot, "/boot/system/develop/lib"));
diff --git a/clang/lib/Driver/ToolChains/Hurd.cpp b/clang/lib/Driver/ToolChains/Hurd.cpp
index 0bc114b90ffc0..efddd4b429869 100644
--- a/clang/lib/Driver/ToolChains/Hurd.cpp
+++ b/clang/lib/Driver/ToolChains/Hurd.cpp
@@ -71,7 +71,7 @@ static StringRef getOSLibDir(const llvm::Triple &Triple, const ArgList &Args) {
 
 Hurd::Hurd(const Driver &D, const llvm::Triple &Triple, const ArgList &Args)
     : Generic_ELF(D, Triple, Args) {
-  GCCInstallation.init(Triple, Args);
+  GCCInstallation.init(Triple, Args, getCXXStdlibTypeInUse());
   Multilibs = GCCInstallation.getMultilibs();
   SelectedMultilibs.assign({GCCInstallation.getMultilib()});
   std::string SysRoot = computeSysRoot();
diff --git a/clang/lib/Driver/ToolChains/Linux.cpp b/clang/lib/Driver/ToolChains/Linux.cpp
index 0767fe6c58796..86bf610026226 100644
--- a/clang/lib/Driver/ToolChains/Linux.cpp
+++ b/clang/lib/Driver/ToolChains/Linux.cpp
@@ -212,7 +212,7 @@ static StringRef getOSLibDir(const llvm::Triple &Triple, const ArgList &Args) {
 
 Linux::Linux(const Driver &D, const llvm::Triple &Triple, const ArgList &Args)
     : Generic_ELF(D, Triple, Args) {
-  GCCInstallation.init(Triple, Args);
+  GCCInstallation.init(Triple, Args, getCXXStdlibTypeInUse());
   Multilibs = GCCInstallation.getMultilibs();
   SelectedMultilibs.assign({GCCInstallation.getMultilib()});
   llvm::Triple::ArchType Arch = Triple.getArch();
diff --git a/clang/lib/Driver/ToolChains/MSP430.cpp b/clang/lib/Driver/ToolChains/MSP430.cpp
index 07e875c64960e..ed515f0f1373c 100644
--- a/clang/lib/Driver/ToolChains/MSP430.cpp
+++ b/clang/lib/Driver/ToolChains/MSP430.cpp
@@ -112,7 +112,7 @@ MSP430ToolChain::MSP430ToolChain(const Driver &D, const llvm::Triple &Triple,
 
   StringRef MultilibSuf = "";
 
-  GCCInstallation.init(Triple, Args);
+  GCCInstallation.init(Triple, Args, getCXXStdlibTypeInUse());
   if (GCCInstallation.isValid()) {
     MultilibSuf = GCCInstallation.getMultilib().gccSuffix();
 
diff --git a/clang/lib/Driver/ToolChains/RISCVToolchain.cpp b/clang/lib/Driver/ToolChains/RISCVToolchain.cpp
index 624099d21ae12..e5d1e550f5453 100644
--- a/clang/lib/Driver/ToolChains/RISCVToolchain.cpp
+++ b/clang/lib/Driver/ToolChains/RISCVToolchain.cpp
@@ -50,7 +50,7 @@ bool RISCVToolChain::hasGCCToolchain(const Driver &D,
 RISCVToolChain::RISCVToolChain(const Driver &D, const llvm::Triple &Triple,
                                const ArgList &Args)
     : Generic_ELF(D, Triple, Args) {
-  GCCInstallation.init(Triple, Args);
+  GCCInstallation.init(Triple, Args, getCXXStdlibTypeInUse());
   if (GCCInstallation.isValid()) {
     Multilibs = GCCInstallation.getMultilibs();
     SelectedMultilibs.assign({GCCInstallation.getMultilib()});
diff --git a/clang/lib/Driver/ToolChains/Solaris.cpp b/clang/lib/Driver/ToolChains/Solaris.cpp
index fd3232b7c1b06..1153c2ac319cb 100644
--- a/clang/lib/Driver/ToolChains/Solaris.cpp
+++ b/clang/lib/Driver/ToolChains/Solaris.cpp
@@ -310,7 +310,7 @@ Solaris::Solaris(const Driver &D, const llvm::Triple &Triple,
                  const ArgList &Args)
     : Generic_ELF(D, Triple, Args) {
 
-  GCCInstallation.init(Triple, Args);
+  GCCInstallation.init(Triple, Args, getCXXStdlibTypeInUse());
 
   StringRef LibSuffix = getSolarisLibSuffix(Triple);
   path_list &Paths = getFilePaths();
diff --git a/clang/test/Driver/Inputs/powerpc64le-linux-gnu-tree/gcc-11.2.0/include/c++/11.2.0/.keep b/clang/test/Driver/Inputs/powerpc64le-linux-gnu-tree/gcc-11.2.0/include/c++/11.2.0/.keep
new file mode 100644
index 0000000000000..e69de29bb2d1d
diff --git a/clang/test/Driver/Inputs/powerpc64le-linux-gnu-tree/gcc-12/include/c++/12/.keep b/clang/test/Driver/Inputs/powerpc64le-linux-gnu-tree/gcc-12/include/c++/12/.keep
new file mode 100644
index 0000000000000..e69de29bb2d1d
diff --git a/clang/test/Driver/gcc-toolchain.cpp b/clang/test/Driver/gcc-toolchain.cpp
index 6d4ad417cfec6..a14e8d00af1ef 100644
--- a/clang/test/Driver/gcc-toolchain.cpp
+++ b/clang/test/Driver/gcc-toolchain.cpp
@@ -6,11 +6,6 @@
 // RUN:   --gcc-toolchain=%S/Inputs/ubuntu_14.04_multiarch_tree/usr -stdlib=libstdc++ --rtlib=libgcc --unwindlib=libgcc -no-pie 2>&1 | \
 // RUN:   FileCheck %s
 //
-// Additionally check that the legacy spelling of the flag works.
-// RUN: %clangxx %s -### --target=x86_64-linux-gnu --sysroot= \
-// RUN:   --gcc-toolchain=%S/Inputs/ubuntu_14.04_multiarch_tree/usr -stdlib=libstdc++ --rtlib=libgcc --unwindlib=libgcc -no-pie 2>&1 | \
-// RUN:   FileCheck %s
-//
 // Test for header search toolchain detection.
 // CHECK: "-internal-isystem"
 // CHECK: "[[TOOLCHAIN:[^"]+]]/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8"



More information about the cfe-commits mailing list