Skip to content

🍒[Clang][Driver] Fix include paths for --sysroot / on Linux #4761

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions clang/include/clang/Driver/ToolChain.h
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,9 @@ class ToolChain {
static void addSystemIncludes(const llvm::opt::ArgList &DriverArgs,
llvm::opt::ArgStringList &CC1Args,
ArrayRef<StringRef> Paths);

static std::string concat(StringRef Path, const Twine &A, const Twine &B = "",
const Twine &C = "", const Twine &D = "");
///@}

public:
Expand Down
8 changes: 8 additions & 0 deletions clang/lib/Driver/ToolChain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -876,6 +876,14 @@ void ToolChain::addExternCSystemIncludeIfExists(const ArgList &DriverArgs,
}
}

/*static*/ std::string ToolChain::concat(StringRef Path, const Twine &A,
const Twine &B, const Twine &C,
const Twine &D) {
SmallString<128> Result(Path);
llvm::sys::path::append(Result, llvm::sys::path::Style::posix, A, B, C, D);
return std::string(Result);
}

std::string ToolChain::detectLibcxxVersion(StringRef IncludePath) const {
std::error_code EC;
int MaxVersion = 0;
Expand Down
22 changes: 11 additions & 11 deletions clang/lib/Driver/ToolChains/Gnu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1957,7 +1957,7 @@ void Generic_GCC::GCCInstallationDetector::init(
if (!VFS.exists(Prefix))
continue;
for (StringRef Suffix : CandidateLibDirs) {
const std::string LibDir = Prefix + Suffix.str();
const std::string LibDir = concat(Prefix, Suffix);
if (!VFS.exists(LibDir))
continue;
// Maybe filter out <libdir>/gcc and <libdir>/gcc-cross.
Expand Down Expand Up @@ -2023,7 +2023,7 @@ void Generic_GCC::GCCInstallationDetector::AddDefaultGCCPrefixes(
// so we need to find those /usr/gcc/*/lib/gcc libdirs and go with
// /usr/gcc/<version> as a prefix.

std::string PrefixDir = SysRoot.str() + "/usr/gcc";
std::string PrefixDir = concat(SysRoot, "/usr/gcc");
std::error_code EC;
for (llvm::vfs::directory_iterator LI = D.getVFS().dir_begin(PrefixDir, EC),
LE;
Expand Down Expand Up @@ -2058,7 +2058,7 @@ void Generic_GCC::GCCInstallationDetector::AddDefaultGCCPrefixes(
Prefixes.push_back("/opt/rh/devtoolset-3/root/usr");
Prefixes.push_back("/opt/rh/devtoolset-2/root/usr");
}
Prefixes.push_back(SysRoot.str() + "/usr");
Prefixes.push_back(concat(SysRoot, "/usr"));
}

/*static*/ void Generic_GCC::GCCInstallationDetector::CollectLibDirsAndTriples(
Expand Down Expand Up @@ -2571,7 +2571,7 @@ bool Generic_GCC::GCCInstallationDetector::ScanGentooConfigs(
const llvm::Triple &TargetTriple, const ArgList &Args,
const SmallVectorImpl<StringRef> &CandidateTriples,
const SmallVectorImpl<StringRef> &CandidateBiarchTriples) {
if (!D.getVFS().exists(D.SysRoot + GentooConfigDir))
if (!D.getVFS().exists(concat(D.SysRoot, GentooConfigDir)))
return false;

for (StringRef CandidateTriple : CandidateTriples) {
Expand All @@ -2590,8 +2590,8 @@ bool Generic_GCC::GCCInstallationDetector::ScanGentooGccConfig(
const llvm::Triple &TargetTriple, const ArgList &Args,
StringRef CandidateTriple, bool NeedsBiarchSuffix) {
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> File =
D.getVFS().getBufferForFile(D.SysRoot + GentooConfigDir + "/config-" +
CandidateTriple.str());
D.getVFS().getBufferForFile(concat(D.SysRoot, GentooConfigDir,
"/config-" + CandidateTriple.str()));
if (File) {
SmallVector<StringRef, 2> Lines;
File.get()->getBuffer().split(Lines, "\n");
Expand All @@ -2602,8 +2602,8 @@ bool Generic_GCC::GCCInstallationDetector::ScanGentooGccConfig(
continue;
// Process the config file pointed to by CURRENT.
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> ConfigFile =
D.getVFS().getBufferForFile(D.SysRoot + GentooConfigDir + "/" +
Line.str());
D.getVFS().getBufferForFile(
concat(D.SysRoot, GentooConfigDir, "/" + Line));
std::pair<StringRef, StringRef> ActiveVersion = Line.rsplit('-');
// List of paths to scan for libraries.
SmallVector<StringRef, 4> GentooScanPaths;
Expand Down Expand Up @@ -2636,7 +2636,7 @@ bool Generic_GCC::GCCInstallationDetector::ScanGentooGccConfig(

// Scan all paths for GCC libraries.
for (const auto &GentooScanPath : GentooScanPaths) {
std::string GentooPath = D.SysRoot + std::string(GentooScanPath);
std::string GentooPath = concat(D.SysRoot, GentooScanPath);
if (D.getVFS().exists(GentooPath + "/crtbegin.o")) {
if (!ScanGCCForMultilibs(TargetTriple, Args, GentooPath,
NeedsBiarchSuffix))
Expand Down Expand Up @@ -2922,9 +2922,9 @@ Generic_GCC::addLibCxxIncludePaths(const llvm::opt::ArgList &DriverArgs,
// If this is a development, non-installed, clang, libcxx will
// not be found at ../include/c++ but it likely to be found at
// one of the following two locations:
if (AddIncludePath(SysRoot + "/usr/local/include"))
if (AddIncludePath(concat(SysRoot, "/usr/local/include")))
return;
if (AddIncludePath(SysRoot + "/usr/include"))
if (AddIncludePath(concat(SysRoot, "/usr/include")))
return;
}

Expand Down
45 changes: 23 additions & 22 deletions clang/lib/Driver/ToolChains/Linux.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,9 @@ std::string Linux::getMultiarchTriple(const Driver &D,
case llvm::Triple::mips64: {
std::string MT = std::string(IsMipsR6 ? "mipsisa64r6" : "mips64") +
"-linux-" + (IsMipsN32Abi ? "gnuabin32" : "gnuabi64");
if (D.getVFS().exists(SysRoot + "/lib/" + MT))
if (D.getVFS().exists(concat(SysRoot, "/lib", MT)))
return MT;
if (D.getVFS().exists(SysRoot + "/lib/mips64-linux-gnu"))
if (D.getVFS().exists(concat(SysRoot, "/lib/mips64-linux-gnu")))
return "mips64-linux-gnu";
break;
}
Expand All @@ -108,14 +108,14 @@ std::string Linux::getMultiarchTriple(const Driver &D,
return "mips64el-linux-android";
std::string MT = std::string(IsMipsR6 ? "mipsisa64r6el" : "mips64el") +
"-linux-" + (IsMipsN32Abi ? "gnuabin32" : "gnuabi64");
if (D.getVFS().exists(SysRoot + "/lib/" + MT))
if (D.getVFS().exists(concat(SysRoot, "/lib", MT)))
return MT;
if (D.getVFS().exists(SysRoot + "/lib/mips64el-linux-gnu"))
if (D.getVFS().exists(concat(SysRoot, "/lib/mips64el-linux-gnu")))
return "mips64el-linux-gnu";
break;
}
case llvm::Triple::ppc:
if (D.getVFS().exists(SysRoot + "/lib/powerpc-linux-gnuspe"))
if (D.getVFS().exists(concat(SysRoot, "/lib/powerpc-linux-gnuspe")))
return "powerpc-linux-gnuspe";
return "powerpc-linux-gnu";
case llvm::Triple::ppcle:
Expand Down Expand Up @@ -263,8 +263,8 @@ Linux::Linux(const Driver &D, const llvm::Triple &Triple, const ArgList &Args)

Generic_GCC::AddMultilibPaths(D, SysRoot, OSLibDir, MultiarchTriple, Paths);

addPathIfExists(D, SysRoot + "/lib/" + MultiarchTriple, Paths);
addPathIfExists(D, SysRoot + "/lib/../" + OSLibDir, Paths);
addPathIfExists(D, concat(SysRoot, "/lib", MultiarchTriple), Paths);
addPathIfExists(D, concat(SysRoot, "/lib/..", OSLibDir), Paths);

if (IsAndroid) {
// Android sysroots contain a library directory for each supported OS
Expand All @@ -275,24 +275,24 @@ Linux::Linux(const Driver &D, const llvm::Triple &Triple, const ArgList &Args)
unsigned Micro;
Triple.getEnvironmentVersion(Major, Minor, Micro);
addPathIfExists(D,
SysRoot + "/usr/lib/" + MultiarchTriple + "/" +
llvm::to_string(Major),
concat(SysRoot, "/usr/lib", MultiarchTriple,
llvm::to_string(Major)),
Paths);
}

addPathIfExists(D, SysRoot + "/usr/lib/" + MultiarchTriple, Paths);
addPathIfExists(D, concat(SysRoot, "/usr/lib", MultiarchTriple), Paths);
// 64-bit OpenEmbedded sysroots may not have a /usr/lib dir. So they cannot
// find /usr/lib64 as it is referenced as /usr/lib/../lib64. So we handle
// this here.
if (Triple.getVendor() == llvm::Triple::OpenEmbedded &&
Triple.isArch64Bit())
addPathIfExists(D, SysRoot + "/usr/" + OSLibDir, Paths);
addPathIfExists(D, concat(SysRoot, "/usr", OSLibDir), Paths);
else
addPathIfExists(D, SysRoot + "/usr/lib/../" + OSLibDir, Paths);
addPathIfExists(D, concat(SysRoot, "/usr/lib/..", OSLibDir), Paths);
if (IsRISCV) {
StringRef ABIName = tools::riscv::getRISCVABI(Args, Triple);
addPathIfExists(D, SysRoot + "/" + OSLibDir + "/" + ABIName, Paths);
addPathIfExists(D, SysRoot + "/usr/" + OSLibDir + "/" + ABIName, Paths);
addPathIfExists(D, concat(SysRoot, "/", OSLibDir, ABIName), Paths);
addPathIfExists(D, concat(SysRoot, "/usr", OSLibDir, ABIName), Paths);
}

Generic_GCC::AddMultiarchPaths(D, SysRoot, OSLibDir, Paths);
Expand All @@ -310,8 +310,8 @@ Linux::Linux(const Driver &D, const llvm::Triple &Triple, const ArgList &Args)
addPathIfExists(D, D.Dir + "/../" + OSLibDir, Paths);
}

addPathIfExists(D, SysRoot + "/lib", Paths);
addPathIfExists(D, SysRoot + "/usr/lib", Paths);
addPathIfExists(D, concat(SysRoot, "/lib"), Paths);
addPathIfExists(D, concat(SysRoot, "/usr/lib"), Paths);
}

ToolChain::RuntimeLibType Linux::GetDefaultRuntimeLibType() const {
Expand Down Expand Up @@ -555,7 +555,7 @@ void Linux::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
return;

// LOCAL_INCLUDE_DIR
addSystemInclude(DriverArgs, CC1Args, SysRoot + "/usr/local/include");
addSystemInclude(DriverArgs, CC1Args, concat(SysRoot, "/usr/local/include"));
// TOOL_INCLUDE_DIR
AddMultilibIncludeArgs(DriverArgs, CC1Args);

Expand All @@ -576,19 +576,20 @@ void Linux::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
// /usr/include.
std::string MultiarchIncludeDir = getMultiarchTriple(D, getTriple(), SysRoot);
if (!MultiarchIncludeDir.empty() &&
D.getVFS().exists(SysRoot + "/usr/include/" + MultiarchIncludeDir))
addExternCSystemInclude(DriverArgs, CC1Args,
SysRoot + "/usr/include/" + MultiarchIncludeDir);
D.getVFS().exists(concat(SysRoot, "/usr/include", MultiarchIncludeDir)))
addExternCSystemInclude(
DriverArgs, CC1Args,
concat(SysRoot, "/usr/include", MultiarchIncludeDir));

if (getTriple().getOS() == llvm::Triple::RTEMS)
return;

// Add an include of '/include' directly. This isn't provided by default by
// system GCCs, but is often used with cross-compiling GCCs, and harmless to
// add even when Clang is acting as-if it were a system compiler.
addExternCSystemInclude(DriverArgs, CC1Args, SysRoot + "/include");
addExternCSystemInclude(DriverArgs, CC1Args, concat(SysRoot, "/include"));

addExternCSystemInclude(DriverArgs, CC1Args, SysRoot + "/usr/include");
addExternCSystemInclude(DriverArgs, CC1Args, concat(SysRoot, "/usr/include"));

if (!DriverArgs.hasArg(options::OPT_nobuiltininc) && getTriple().isMusl())
addSystemInclude(DriverArgs, CC1Args, ResourceDirInclude);
Expand Down
Empty file.
31 changes: 30 additions & 1 deletion clang/test/Driver/linux-header-search.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,22 @@
// CHECK-BASIC-LIBCXX-SYSROOT: "-internal-isystem" "[[SYSROOT]]/usr/include/x86_64-unknown-linux-gnu/c++/v1"
// CHECK-BASIC-LIBCXX-SYSROOT: "-internal-isystem" "[[SYSROOT]]/usr/include/c++/v1"
// CHECK-BASIC-LIBCXX-SYSROOT: "-internal-isystem" "[[SYSROOT]]/usr/local/include"

// Test include paths when the sysroot path ends with `/`.
// RUN: %clang -### %s -fsyntax-only 2>&1 \
// RUN: --target=x86_64-unknown-linux-gnu \
// RUN: -stdlib=libc++ \
// RUN: -ccc-install-dir %S/Inputs/basic_linux_tree/usr/bin \
// RUN: -resource-dir=%S/Inputs/resource_dir \
// RUN: --sysroot=%S/Inputs/basic_linux_libcxx_tree/ \
// RUN: --gcc-toolchain="" \
// RUN: | FileCheck --check-prefix=CHECK-BASIC-LIBCXX-SYSROOT-SLASH %s
// CHECK-BASIC-LIBCXX-SYSROOT-SLASH: "-cc1"
// CHECK-BASIC-LIBCXX-SYSROOT-SLASH-SAME: "-isysroot" "[[SYSROOT:[^"]+/]]"
// CHECK-BASIC-LIBCXX-SYSROOT-SLASH-SAME: "-internal-isystem" "[[SYSROOT]]usr/include/x86_64-unknown-linux-gnu/c++/v1"
// CHECK-BASIC-LIBCXX-SYSROOT-SLASH-SAME: "-internal-isystem" "[[SYSROOT]]usr/include/c++/v1"
// CHECK-BASIC-LIBCXX-SYSROOT-SLASH-SAME: "-internal-isystem" "[[SYSROOT]]usr/local/include"

// RUN: %clang -no-canonical-prefixes %s -### -fsyntax-only 2>&1 \
// RUN: -target x86_64-unknown-linux-gnu \
// RUN: -stdlib=libc++ \
Expand Down Expand Up @@ -56,7 +72,20 @@
// CHECK-BASIC-LIBCXXV2-INSTALL: "-internal-isystem" "[[SYSROOT]]/usr/bin/../include/x86_64-unknown-linux-gnu/c++/v2"
// CHECK-BASIC-LIBCXXV2-INSTALL: "-internal-isystem" "[[SYSROOT]]/usr/bin/../include/c++/v2"
// CHECK-BASIC-LIBCXXV2-INSTALL: "-internal-isystem" "[[SYSROOT]]/usr/local/include"
//

// Test Linux with libstdc++ when the sysroot path ends with `/`.
// RUN: %clang -### %s -fsyntax-only 2>&1 \
// RUN: --target=x86_64-unknown-linux-gnu \
// RUN: -stdlib=libstdc++ \
// RUN: -ccc-install-dir %S/Inputs/basic_linux_tree/usr/bin \
// RUN: -resource-dir=%S/Inputs/resource_dir \
// RUN: --sysroot=%S/Inputs/basic_linux_libstdcxx_tree/ \
// RUN: --gcc-toolchain="" \
// RUN: | FileCheck --check-prefix=CHECK-BASIC-LIBSTDCXX-SYSROOT-SLASH %s
// CHECK-BASIC-LIBSTDCXX-SYSROOT-SLASH: "-cc1"
// CHECK-BASIC-LIBSTDCXX-SYSROOT-SLASH-SAME: "-isysroot" "[[SYSROOT:[^"]+/]]"
// CHECK-BASIC-LIBSTDCXX-SYSROOT-SLASH-SAME: "-internal-isystem" "[[SYSROOT]]usr/lib/gcc/x86_64-unknown-linux-gnu/4.8/../../../../x86_64-unknown-linux-gnu/include"

// Test Linux with both libc++ and libstdc++ installed.
// RUN: %clang -no-canonical-prefixes %s -### -fsyntax-only 2>&1 \
// RUN: -target x86_64-unknown-linux-gnu \
Expand Down