Reapply "[Clang] Take libstdc++ into account during GCC detection" #145056 (#154487)

The Generic_GCC::GCCInstallationDetector class picks the GCC installation directory with the largest version number. Since the location of the libstdc++ include directories is tied to the GCC version, this can break C++ compilation if the libstdc++ headers for this particular GCC version are not available. Linux distributions tend to package the libstdc++ headers separately from GCC. This frequently leads to situations in which a newer version of GCC gets installed as a dependency of another package without installing the corresponding libstdc++ package. Clang then fails to compile C++ code because it cannot find the libstdc++ headers. Since libstdc++ headers are in fact installed on the system, the GCC installation continues to work, the user may not be aware of the details of the GCC detection, and the compiler does not recognize the situation and emit a warning, this behavior can be hard to understand - as witnessed by many related bug reports over the years.

The goal of this work is to change the GCC detection to prefer GCC installations that contain libstdc++ include directories over those which do not. This should happen regardless of the input language since picking different GCC installations for a build that mixes C and C++ might lead to incompatibilities.
Any change to the GCC installation detection will probably have a negative impact on some users. For instance, for a C user who relies on using the GCC installation with the largest version number, it might become necessary to use the --gcc-install-dir option to ensure that this GCC version is selected.
This seems like an acceptable trade-off given that the situation for users who do not have any special demands on the particular GCC installation directory would be improved significantly.
 
This patch does not yet change the automatic GCC installation directory choice. Instead, it does introduce a warning that informs the user about the future change if the chosen GCC installation directory differs from the one that would be chosen if the libstdc++ headers are taken into account.

See also this related Discourse discussion: https://discourse.llvm.org/t/rfc-take-libstdc-into-account-during-gcc-detection/86992.

This patch reapplies #145056. The test in the original PR did not specify a target in the clang RUN line and used a wrong way of piping to FileCheck.
This commit is contained in:
Frederik Harwath 2025-08-22 07:39:11 +02:00 committed by GitHub
parent 630712f4c1
commit d6fae7f921
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
37 changed files with 338 additions and 125 deletions

View File

@ -37,6 +37,22 @@ latest release, please see the `Clang Web Site <https://clang.llvm.org>`_ or the
Potentially Breaking Changes
============================
- Clang will now emit a warning if the auto-detected GCC installation
directory (i.e. the one with the largest version number) does not
contain libstdc++ include directories although a "complete" GCC
installation directory containing the include directories is
available. It is planned to change the auto-detection to prefer the
"complete" directory in the future. The warning will disappear if
the libstdc++ include directories are either installed or removed
for all GCC installation directories considered by the
auto-detection; see the output of ``clang -v`` for a list of those
directories. If the GCC installations cannot be modified and
maintaining the current choice of the auto-detection is desired, the
GCC installation directory can be selected explicitly using the
``--gcc-install-dir`` command line argument. This will silence the
warning. It can also be disabled using the
``-Wno-gcc-install-dir-libstdcxx`` command line flag.
C/C++ Language Potentially Breaking Changes
-------------------------------------------

View File

@ -885,4 +885,9 @@ def warn_drv_openacc_without_cir
: Warning<"OpenACC directives will result in no runtime behavior; use "
"-fclangir to enable runtime effect">,
InGroup<SourceUsesOpenACC>;
def warn_drv_gcc_install_dir_libstdcxx : Warning<
"future releases of the clang compiler will prefer GCC installations "
"containing libstdc++ include directories; '%0' would be chosen over '%1'">,
InGroup<DiagGroup<"gcc-install-dir-libstdcxx">>;
}

View File

@ -224,9 +224,6 @@ protected:
static void addSystemFrameworkInclude(const llvm::opt::ArgList &DriverArgs,
llvm::opt::ArgStringList &CC1Args,
const Twine &Path);
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);
@ -246,6 +243,9 @@ protected:
///@}
public:
static void addSystemInclude(const llvm::opt::ArgList &DriverArgs,
llvm::opt::ArgStringList &CC1Args,
const Twine &Path);
virtual ~ToolChain();
// Accessors

View File

@ -1409,13 +1409,6 @@ void ToolChain::addSystemFrameworkInclude(const llvm::opt::ArgList &DriverArgs,
CC1Args.push_back(DriverArgs.MakeArgString(Path));
}
/// Utility function to add a system include directory to CC1 arguments.
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.
///
@ -1438,6 +1431,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 framework directories to CC1.
void ToolChain::addSystemFrameworkIncludes(const ArgList &DriverArgs,
ArgStringList &CC1Args,

View File

@ -2123,10 +2123,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;
@ -2186,7 +2187,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))
@ -2214,7 +2215,7 @@ void Generic_GCC::GCCInstallationDetector::init(
}
// Skip other prefixes once a GCC installation is found.
if (Version > VersionZero)
if (SelectedInstallation.Version > VersionZero)
break;
}
}
@ -2223,14 +2224,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 {
@ -2768,14 +2772,50 @@ 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;
}
bool Generic_GCC::GCCInstallationDetector::SelectGCCInstallationDirectory(
const SmallVector<Generic_GCC::GCCInstallCandidate, 3> &Installations,
const ArgList &Args,
Generic_GCC::GCCInstallCandidate &SelectedInstallation) const {
if (Installations.empty())
return false;
SelectedInstallation =
*max_element(Installations, [](const auto &Max, const auto &I) {
return I.Version > Max.Version;
});
// FIXME Start selecting installation with libstdc++ in clang 22,
// using the current way of selecting the installation as a fallback
// only. For now, warn if the installation with libstdc++ differs
// from SelectedInstallation.
const GCCInstallCandidate *InstallWithIncludes = nullptr;
for (const auto &I : Installations) {
if ((!InstallWithIncludes || I.Version > InstallWithIncludes->Version) &&
GCCInstallationHasLibStdcxxIncludePaths(I, Args))
InstallWithIncludes = &I;
}
if (InstallWithIncludes && SelectedInstallation.GCCInstallPath !=
InstallWithIncludes->GCCInstallPath)
D.Diag(diag::warn_drv_gcc_install_dir_libstdcxx)
<< InstallWithIncludes->GCCInstallPath
<< SelectedInstallation.GCCInstallPath;
// TODO Warn if SelectedInstallation does not contain libstdc++ includes
// although compiler flags indicate that it is required (C++ compilation,
// libstdc++ not explicitly disabled).
return true;
}
void Generic_GCC::GCCInstallationDetector::ScanLibDirForGCCTriple(
const llvm::Triple &TargetTriple, const ArgList &Args,
const std::string &LibDir, StringRef CandidateTriple,
@ -2805,6 +2845,7 @@ void Generic_GCC::GCCInstallationDetector::ScanLibDirForGCCTriple(
TargetTriple.getVendor() == llvm::Triple::Freescale ||
TargetTriple.getVendor() == llvm::Triple::OpenEmbedded}};
SmallVector<GCCInstallCandidate, 3> Installations;
for (auto &Suffix : Suffixes) {
if (!Suffix.Active)
continue;
@ -2822,23 +2863,31 @@ 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();
IsValid = true;
Installation.GCCInstallPath =
(LibDir + "/" + LibSuffix + "/" + VersionText).str();
Installation.GCCParentLibPath =
(Installation.GCCInstallPath + "/../" + Suffix.ReversePath).str();
Installation.SelectedMultilib = getMultilib();
Installations.push_back(Installation);
}
}
IsValid |=
SelectGCCInstallationDirectory(Installations, Args, SelectedInstallation);
}
bool Generic_GCC::GCCInstallationDetector::ScanGentooConfigs(
@ -2916,10 +2965,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;
}
@ -3122,8 +3173,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) {
@ -3210,12 +3262,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
@ -3227,39 +3281,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;
@ -3267,22 +3330,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
@ -3297,20 +3362,50 @@ 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::
GCCInstallationHasLibStdcxxIncludePaths(
const GCCInstallCandidate &GCCInstallation,
const llvm::opt::ArgList &DriverArgs) const {
StringRef DebianMultiarch =
TripleToDebianMultiarch(GCCInstallation.getTriple());
// The following function checks for libstdc++ include paths and
// adds them to the provided argument list. Here we just need the
// check.
llvm::opt::ArgStringList dummyCC1Args;
return GCCInstallation.addGCCLibStdCxxIncludePaths(
D.getVFS(), DriverArgs, dummyCC1Args, DebianMultiarch);
}
bool Generic_GCC::addGCCLibStdCxxIncludePaths(
const llvm::opt::ArgList &DriverArgs,
llvm::opt::ArgStringList &CC1Args) const {
assert(GCCInstallation.isValid());
// Detect Debian g++-multiarch-incdir.diff.
StringRef DebianMultiarch =
GCCInstallation.TripleToDebianMultiarch(GCCInstallation.getTriple());
return GCCInstallation.getSelectedInstallation().addGCCLibStdCxxIncludePaths(
getVFS(), DriverArgs, CC1Args, DebianMultiarch);
}
void
Generic_GCC::addLibStdCxxIncludePaths(const llvm::opt::ArgList &DriverArgs,
llvm::opt::ArgStringList &CC1Args) const {
if (GCCInstallation.isValid()) {
addGCCLibStdCxxIncludePaths(DriverArgs, CC1Args,
GCCInstallation.getTriple().str());
}
if (!GCCInstallation.isValid())
return;
GCCInstallation.getSelectedInstallation().addGCCLibStdCxxIncludePaths(
getVFS(), DriverArgs, CC1Args, GCCInstallation.getTriple().str());
}
llvm::opt::DerivedArgList *

View File

@ -184,46 +184,18 @@ public:
bool operator>=(const GCCVersion &RHS) const { return !(*this < RHS); }
};
/// This is a class to find a viable GCC installation for Clang to
/// use.
///
/// This class tries to find a GCC installation on the system, and report
/// information about it. It starts from the host information provided to the
/// Driver, and has logic for fuzzing that where appropriate.
class GCCInstallationDetector {
bool IsValid;
llvm::Triple GCCTriple;
const Driver &D;
struct GCCInstallCandidate {
// FIXME: These might be better as path objects.
std::string GCCInstallPath;
std::string GCCParentLibPath;
llvm::Triple GCCTriple;
/// 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;
/// The set of multilibs that the detected installation supports.
MultilibSet Multilibs;
// Gentoo-specific toolchain configurations are stored here.
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);
/// Check whether we detected a valid GCC install.
bool isValid() const { return IsValid; }
/// Get the GCC triple for the detected install.
const llvm::Triple &getTriple() const { return GCCTriple; }
@ -236,6 +208,88 @@ public:
/// 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.
///
/// This class tries to find a GCC installation on the system, and report
/// information about it. It starts from the host information provided to the
/// Driver, and has logic for fuzzing that where appropriate.
class GCCInstallationDetector {
bool IsValid;
const Driver &D;
GCCInstallCandidate SelectedInstallation;
/// On Biarch systems, this corresponds to the default multilib when
/// targeting the non-default multilib. Otherwise, it is empty.
std::optional<Multilib> BiarchSibling;
// 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;
/// The set of multilibs that the detected installation supports.
MultilibSet Multilibs;
// Gentoo-specific toolchain configurations are stored here.
const std::string GentooConfigDir = "/etc/env.d/gcc";
public:
/// Function for converting a triple to a Debian multiarch. The
/// toolchains use this to adjust the target specific component of
/// include paths for Debian.
std::function<StringRef(const llvm::Triple &)> TripleToDebianMultiarch =
[](const llvm::Triple &T) {
StringRef S = T.str();
return S;
};
explicit GCCInstallationDetector(const Driver &D) : IsValid(false), D(D) {}
void init(const llvm::Triple &TargetTriple, const llvm::opt::ArgList &Args);
// TODO Replace isValid by changing SelectedInstallation into
// std::optional<SelectedInstallation>
// and move all accessors for fields of GCCInstallCandidate into
// that struct.
/// 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 SelectedInstallation.GCCTriple;
}
/// Get the detected GCC installation path.
StringRef getInstallPath() const {
return SelectedInstallation.GCCInstallPath;
}
/// Get the detected GCC parent lib path.
StringRef getParentLibPath() const {
return SelectedInstallation.GCCParentLibPath;
}
/// Get the detected Multilib
const Multilib &getMultilib() const {
return SelectedInstallation.SelectedMultilib;
}
/// Get the whole MultilibSet
const MultilibSet &getMultilibs() const { return Multilibs; }
@ -244,7 +298,9 @@ public:
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,6 +318,19 @@ public:
SmallVectorImpl<std::string> &Prefixes,
StringRef SysRoot);
/// Checks if the \p GCCInstallation has libstdc++ include
/// directories.
bool GCCInstallationHasLibStdcxxIncludePaths(
const GCCInstallCandidate &GCCInstallation,
const llvm::opt::ArgList &DriverArgs) const;
/// Select a GCC installation directory from \p Installations and
/// set \p SelectedInstallation accordingly.
bool SelectGCCInstallationDirectory(
const SmallVector<GCCInstallCandidate, 3> &Installations,
const llvm::opt::ArgList &Args,
GCCInstallCandidate &SelectedInstallation) const;
bool ScanGCCForMultilibs(const llvm::Triple &TargetTriple,
const llvm::opt::ArgList &Args, StringRef Path,
bool NeedsBiarchSuffix = false);
@ -348,8 +417,7 @@ protected:
llvm::opt::ArgStringList &CC1Args) const;
bool addGCCLibStdCxxIncludePaths(const llvm::opt::ArgList &DriverArgs,
llvm::opt::ArgStringList &CC1Args,
StringRef DebianMultiarch) const;
llvm::opt::ArgStringList &CC) const;
bool addLibStdCXXIncludePaths(Twine IncludeDir, StringRef Triple,
Twine IncludeSuffix,

View File

@ -71,6 +71,13 @@ 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.TripleToDebianMultiarch = [](const llvm::Triple &T) {
StringRef TripleStr = T.str();
StringRef DebianMultiarch =
T.getArch() == llvm::Triple::x86 ? "i386-gnu" : TripleStr;
return DebianMultiarch;
};
GCCInstallation.init(Triple, Args);
Multilibs = GCCInstallation.getMultilibs();
SelectedMultilibs.assign({GCCInstallation.getMultilib()});
@ -207,12 +214,7 @@ void Hurd::addLibStdCxxIncludePaths(const llvm::opt::ArgList &DriverArgs,
if (!GCCInstallation.isValid())
return;
StringRef TripleStr = GCCInstallation.getTriple().str();
StringRef DebianMultiarch =
GCCInstallation.getTriple().getArch() == llvm::Triple::x86 ? "i386-gnu"
: TripleStr;
addGCCLibStdCxxIncludePaths(DriverArgs, CC1Args, DebianMultiarch);
addGCCLibStdCxxIncludePaths(DriverArgs, CC1Args);
}
void Hurd::addExtraOpts(llvm::opt::ArgStringList &CmdArgs) const {

View File

@ -211,6 +211,13 @@ 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.TripleToDebianMultiarch = [](const llvm::Triple &T) {
StringRef TripleStr = T.str();
StringRef DebianMultiarch =
T.getArch() == llvm::Triple::x86 ? "i386-linux-gnu" : TripleStr;
return DebianMultiarch;
};
GCCInstallation.init(Triple, Args);
Multilibs = GCCInstallation.getMultilibs();
SelectedMultilibs.assign({GCCInstallation.getMultilib()});
@ -693,22 +700,15 @@ void Linux::addLibStdCxxIncludePaths(const llvm::opt::ArgList &DriverArgs,
if (!GCCInstallation.isValid())
return;
// Detect Debian g++-multiarch-incdir.diff.
StringRef TripleStr = GCCInstallation.getTriple().str();
StringRef DebianMultiarch =
GCCInstallation.getTriple().getArch() == llvm::Triple::x86
? "i386-linux-gnu"
: TripleStr;
// Try generic GCC detection first.
if (Generic_GCC::addGCCLibStdCxxIncludePaths(DriverArgs, CC1Args,
DebianMultiarch))
if (Generic_GCC::addGCCLibStdCxxIncludePaths(DriverArgs, CC1Args))
return;
StringRef LibDir = GCCInstallation.getParentLibPath();
const Multilib &Multilib = GCCInstallation.getMultilib();
const GCCVersion &Version = GCCInstallation.getVersion();
StringRef TripleStr = GCCInstallation.getTriple().str();
const std::string LibStdCXXIncludePathCandidates[] = {
// Android standalone toolchain has C++ headers in yet another place.
LibDir.str() + "/../" + TripleStr.str() + "/include/c++/" + Version.Text,

View File

@ -193,10 +193,8 @@ void Managarm::addLibStdCxxIncludePaths(
if (!GCCInstallation.isValid())
return;
StringRef TripleStr = GCCInstallation.getTriple().str();
// Try generic GCC detection.
Generic_GCC::addGCCLibStdCxxIncludePaths(DriverArgs, CC1Args, TripleStr);
addGCCLibStdCxxIncludePaths(DriverArgs, CC1Args);
}
SanitizerMask Managarm::getSupportedSanitizers() const {

View File

@ -0,0 +1,28 @@
// UNSUPPORTED: system-windows
// This file tests that the GCC installation directory detection takes
// the libstdc++ includes into account. In each directory
// Inputs/gcc_toolchain_libstdcxx/gccX, the installation directory for
// gcc X should be picked in the future since it is the directory with
// the largest version number which also contains the libstdc++
// include directory.
// RUN: %clang --target=x86_64-linux-gnu --gcc-toolchain=%S/Inputs/gcc_toolchain_libstdcxx/gcc10/usr -v 2>&1 | FileCheck %s --check-prefix=GCC10
// GCC10: clang: warning: future releases of the clang compiler will prefer GCC installations containing libstdc++ include directories; '[[PREFIX:.*gcc_toolchain_libstdcxx/gcc10/usr/lib/gcc/x86_64-linux-gnu]]/10' would be chosen over '[[PREFIX]]/12' [-Wgcc-install-dir-libstdcxx]
// GCC10: Found candidate GCC installation: [[PREFIX]]/10
// GCC10: Found candidate GCC installation: [[PREFIX]]/11
// GCC10: Found candidate GCC installation: [[PREFIX]]/12
// GCC10: Selected GCC installation: [[PREFIX]]/12
// RUN: %clang --target=x86_64-linux-gnu --gcc-toolchain=%S/Inputs/gcc_toolchain_libstdcxx/gcc11/usr -v 2>&1 | FileCheck %s --check-prefix=ONLY_GCC11
// ONLY_GCC11: clang: warning: future releases of the clang compiler will prefer GCC installations containing libstdc++ include directories; '[[PREFIX:.*gcc_toolchain_libstdcxx/gcc11/usr/lib/gcc/x86_64-linux-gnu]]/11' would be chosen over '[[PREFIX]]/12' [-Wgcc-install-dir-libstdcxx]
// ONLY_GCC11: Found candidate GCC installation: [[PREFIX]]/10
// ONLY_GCC11: Found candidate GCC installation: [[PREFIX]]/11
// ONLY_GCC11: Found candidate GCC installation: [[PREFIX]]/12
// ONLY_GCC11: Selected GCC installation: [[PREFIX]]/12
// RUN: %clang --target=x86_64-linux-gnu --gcc-toolchain=%S/Inputs/gcc_toolchain_libstdcxx/gcc12/usr -v 2>&1 | FileCheck %s --check-prefix=GCC12
// GCC12: Found candidate GCC installation: [[PREFIX:.*gcc_toolchain_libstdcxx/gcc12/usr/lib/gcc/x86_64-linux-gnu]]/10
// GCC12: Found candidate GCC installation: [[PREFIX]]/11
// GCC12: Found candidate GCC installation: [[PREFIX]]/12
// GCC12: Selected GCC installation: [[PREFIX]]/12