Skip to content

Commit e917801

Browse files
committed
[Clang][Driver] Fix include paths for --sysroot / on Linux
Currently if `--sysroot /` is passed to the Clang driver, the include paths generated by the Clang driver will start with a double slash: `//usr/include/...`. If VFS is used to inject files into the include paths (for example, the Swift compiler does this), VFS will get confused and the injected files won't be visible. This change makes sure that the include paths start with a single slash. Fixes #28283. Differential Revision: https://reviews.llvm.org/D126289
1 parent 684c080 commit e917801

File tree

7 files changed

+77
-36
lines changed

7 files changed

+77
-36
lines changed

clang/include/clang/Driver/ToolChain.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,9 @@ class ToolChain {
215215
static void addSystemIncludes(const llvm::opt::ArgList &DriverArgs,
216216
llvm::opt::ArgStringList &CC1Args,
217217
ArrayRef<StringRef> Paths);
218+
219+
static std::string concat(StringRef Path, const Twine &A, const Twine &B = "",
220+
const Twine &C = "", const Twine &D = "");
218221
///@}
219222

220223
public:

clang/lib/Driver/ToolChain.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -942,6 +942,14 @@ void ToolChain::addExternCSystemIncludeIfExists(const ArgList &DriverArgs,
942942
}
943943
}
944944

945+
/*static*/ std::string ToolChain::concat(StringRef Path, const Twine &A,
946+
const Twine &B, const Twine &C,
947+
const Twine &D) {
948+
SmallString<128> Result(Path);
949+
llvm::sys::path::append(Result, llvm::sys::path::Style::posix, A, B, C, D);
950+
return std::string(Result);
951+
}
952+
945953
std::string ToolChain::detectLibcxxVersion(StringRef IncludePath) const {
946954
std::error_code EC;
947955
int MaxVersion = 0;

clang/lib/Driver/ToolChains/Gnu.cpp

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2038,7 +2038,7 @@ void Generic_GCC::GCCInstallationDetector::init(
20382038
if (!VFS.exists(Prefix))
20392039
continue;
20402040
for (StringRef Suffix : CandidateLibDirs) {
2041-
const std::string LibDir = Prefix + Suffix.str();
2041+
const std::string LibDir = concat(Prefix, Suffix);
20422042
if (!VFS.exists(LibDir))
20432043
continue;
20442044
// Maybe filter out <libdir>/gcc and <libdir>/gcc-cross.
@@ -2104,7 +2104,7 @@ void Generic_GCC::GCCInstallationDetector::AddDefaultGCCPrefixes(
21042104
// so we need to find those /usr/gcc/*/lib/gcc libdirs and go with
21052105
// /usr/gcc/<version> as a prefix.
21062106

2107-
std::string PrefixDir = SysRoot.str() + "/usr/gcc";
2107+
std::string PrefixDir = concat(SysRoot, "/usr/gcc");
21082108
std::error_code EC;
21092109
for (llvm::vfs::directory_iterator LI = D.getVFS().dir_begin(PrefixDir, EC),
21102110
LE;
@@ -2144,7 +2144,7 @@ void Generic_GCC::GCCInstallationDetector::AddDefaultGCCPrefixes(
21442144
}
21452145

21462146
// Fall back to /usr which is used by most non-Solaris systems.
2147-
Prefixes.push_back(SysRoot.str() + "/usr");
2147+
Prefixes.push_back(concat(SysRoot, "/usr"));
21482148
}
21492149

21502150
/*static*/ void Generic_GCC::GCCInstallationDetector::CollectLibDirsAndTriples(
@@ -2667,7 +2667,7 @@ bool Generic_GCC::GCCInstallationDetector::ScanGentooConfigs(
26672667
const llvm::Triple &TargetTriple, const ArgList &Args,
26682668
const SmallVectorImpl<StringRef> &CandidateTriples,
26692669
const SmallVectorImpl<StringRef> &CandidateBiarchTriples) {
2670-
if (!D.getVFS().exists(D.SysRoot + GentooConfigDir))
2670+
if (!D.getVFS().exists(concat(D.SysRoot, GentooConfigDir)))
26712671
return false;
26722672

26732673
for (StringRef CandidateTriple : CandidateTriples) {
@@ -2686,8 +2686,8 @@ bool Generic_GCC::GCCInstallationDetector::ScanGentooGccConfig(
26862686
const llvm::Triple &TargetTriple, const ArgList &Args,
26872687
StringRef CandidateTriple, bool NeedsBiarchSuffix) {
26882688
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> File =
2689-
D.getVFS().getBufferForFile(D.SysRoot + GentooConfigDir + "/config-" +
2690-
CandidateTriple.str());
2689+
D.getVFS().getBufferForFile(concat(D.SysRoot, GentooConfigDir,
2690+
"/config-" + CandidateTriple.str()));
26912691
if (File) {
26922692
SmallVector<StringRef, 2> Lines;
26932693
File.get()->getBuffer().split(Lines, "\n");
@@ -2698,8 +2698,8 @@ bool Generic_GCC::GCCInstallationDetector::ScanGentooGccConfig(
26982698
continue;
26992699
// Process the config file pointed to by CURRENT.
27002700
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> ConfigFile =
2701-
D.getVFS().getBufferForFile(D.SysRoot + GentooConfigDir + "/" +
2702-
Line.str());
2701+
D.getVFS().getBufferForFile(
2702+
concat(D.SysRoot, GentooConfigDir, "/" + Line));
27032703
std::pair<StringRef, StringRef> ActiveVersion = Line.rsplit('-');
27042704
// List of paths to scan for libraries.
27052705
SmallVector<StringRef, 4> GentooScanPaths;
@@ -2732,7 +2732,7 @@ bool Generic_GCC::GCCInstallationDetector::ScanGentooGccConfig(
27322732

27332733
// Scan all paths for GCC libraries.
27342734
for (const auto &GentooScanPath : GentooScanPaths) {
2735-
std::string GentooPath = D.SysRoot + std::string(GentooScanPath);
2735+
std::string GentooPath = concat(D.SysRoot, GentooScanPath);
27362736
if (D.getVFS().exists(GentooPath + "/crtbegin.o")) {
27372737
if (!ScanGCCForMultilibs(TargetTriple, Args, GentooPath,
27382738
NeedsBiarchSuffix))
@@ -3025,9 +3025,9 @@ Generic_GCC::addLibCxxIncludePaths(const llvm::opt::ArgList &DriverArgs,
30253025
// If this is a development, non-installed, clang, libcxx will
30263026
// not be found at ../include/c++ but it likely to be found at
30273027
// one of the following two locations:
3028-
if (AddIncludePath(SysRoot + "/usr/local/include"))
3028+
if (AddIncludePath(concat(SysRoot, "/usr/local/include")))
30293029
return;
3030-
if (AddIncludePath(SysRoot + "/usr/include"))
3030+
if (AddIncludePath(concat(SysRoot, "/usr/include")))
30313031
return;
30323032
}
30333033

clang/lib/Driver/ToolChains/Linux.cpp

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,9 @@ std::string Linux::getMultiarchTriple(const Driver &D,
9797
case llvm::Triple::mips64: {
9898
std::string MT = std::string(IsMipsR6 ? "mipsisa64r6" : "mips64") +
9999
"-linux-" + (IsMipsN32Abi ? "gnuabin32" : "gnuabi64");
100-
if (D.getVFS().exists(SysRoot + "/lib/" + MT))
100+
if (D.getVFS().exists(concat(SysRoot, "/lib", MT)))
101101
return MT;
102-
if (D.getVFS().exists(SysRoot + "/lib/mips64-linux-gnu"))
102+
if (D.getVFS().exists(concat(SysRoot, "/lib/mips64-linux-gnu")))
103103
return "mips64-linux-gnu";
104104
break;
105105
}
@@ -108,14 +108,14 @@ std::string Linux::getMultiarchTriple(const Driver &D,
108108
return "mips64el-linux-android";
109109
std::string MT = std::string(IsMipsR6 ? "mipsisa64r6el" : "mips64el") +
110110
"-linux-" + (IsMipsN32Abi ? "gnuabin32" : "gnuabi64");
111-
if (D.getVFS().exists(SysRoot + "/lib/" + MT))
111+
if (D.getVFS().exists(concat(SysRoot, "/lib", MT)))
112112
return MT;
113-
if (D.getVFS().exists(SysRoot + "/lib/mips64el-linux-gnu"))
113+
if (D.getVFS().exists(concat(SysRoot, "/lib/mips64el-linux-gnu")))
114114
return "mips64el-linux-gnu";
115115
break;
116116
}
117117
case llvm::Triple::ppc:
118-
if (D.getVFS().exists(SysRoot + "/lib/powerpc-linux-gnuspe"))
118+
if (D.getVFS().exists(concat(SysRoot, "/lib/powerpc-linux-gnuspe")))
119119
return "powerpc-linux-gnuspe";
120120
return "powerpc-linux-gnu";
121121
case llvm::Triple::ppcle:
@@ -269,38 +269,38 @@ Linux::Linux(const Driver &D, const llvm::Triple &Triple, const ArgList &Args)
269269
// used. We need add both libo32 and /lib.
270270
if (Arch == llvm::Triple::mips || Arch == llvm::Triple::mipsel) {
271271
Generic_GCC::AddMultilibPaths(D, SysRoot, "libo32", MultiarchTriple, Paths);
272-
addPathIfExists(D, SysRoot + "/libo32", Paths);
273-
addPathIfExists(D, SysRoot + "/usr/libo32", Paths);
272+
addPathIfExists(D, concat(SysRoot, "/libo32"), Paths);
273+
addPathIfExists(D, concat(SysRoot, "/usr/libo32"), Paths);
274274
}
275275
Generic_GCC::AddMultilibPaths(D, SysRoot, OSLibDir, MultiarchTriple, Paths);
276276

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

280280
if (IsAndroid) {
281281
// Android sysroots contain a library directory for each supported OS
282282
// version as well as some unversioned libraries in the usual multiarch
283283
// directory.
284284
addPathIfExists(
285285
D,
286-
SysRoot + "/usr/lib/" + MultiarchTriple + "/" +
287-
llvm::to_string(Triple.getEnvironmentVersion().getMajor()),
286+
concat(SysRoot, "/usr/lib", MultiarchTriple,
287+
llvm::to_string(Triple.getEnvironmentVersion().getMajor())),
288288
Paths);
289289
}
290290

291-
addPathIfExists(D, SysRoot + "/usr/lib/" + MultiarchTriple, Paths);
291+
addPathIfExists(D, concat(SysRoot, "/usr/lib", MultiarchTriple), Paths);
292292
// 64-bit OpenEmbedded sysroots may not have a /usr/lib dir. So they cannot
293293
// find /usr/lib64 as it is referenced as /usr/lib/../lib64. So we handle
294294
// this here.
295295
if (Triple.getVendor() == llvm::Triple::OpenEmbedded &&
296296
Triple.isArch64Bit())
297-
addPathIfExists(D, SysRoot + "/usr/" + OSLibDir, Paths);
297+
addPathIfExists(D, concat(SysRoot, "/usr", OSLibDir), Paths);
298298
else
299-
addPathIfExists(D, SysRoot + "/usr/lib/../" + OSLibDir, Paths);
299+
addPathIfExists(D, concat(SysRoot, "/usr/lib/..", OSLibDir), Paths);
300300
if (IsRISCV) {
301301
StringRef ABIName = tools::riscv::getRISCVABI(Args, Triple);
302-
addPathIfExists(D, SysRoot + "/" + OSLibDir + "/" + ABIName, Paths);
303-
addPathIfExists(D, SysRoot + "/usr/" + OSLibDir + "/" + ABIName, Paths);
302+
addPathIfExists(D, concat(SysRoot, "/", OSLibDir, ABIName), Paths);
303+
addPathIfExists(D, concat(SysRoot, "/usr", OSLibDir, ABIName), Paths);
304304
}
305305

306306
Generic_GCC::AddMultiarchPaths(D, SysRoot, OSLibDir, Paths);
@@ -312,8 +312,8 @@ Linux::Linux(const Driver &D, const llvm::Triple &Triple, const ArgList &Args)
312312
D.getVFS().exists(D.Dir + "/../lib/libc++.so"))
313313
addPathIfExists(D, D.Dir + "/../lib", Paths);
314314

315-
addPathIfExists(D, SysRoot + "/lib", Paths);
316-
addPathIfExists(D, SysRoot + "/usr/lib", Paths);
315+
addPathIfExists(D, concat(SysRoot, "/lib"), Paths);
316+
addPathIfExists(D, concat(SysRoot, "/usr/lib"), Paths);
317317
}
318318

319319
ToolChain::RuntimeLibType Linux::GetDefaultRuntimeLibType() const {
@@ -586,7 +586,7 @@ void Linux::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
586586
return;
587587

588588
// LOCAL_INCLUDE_DIR
589-
addSystemInclude(DriverArgs, CC1Args, SysRoot + "/usr/local/include");
589+
addSystemInclude(DriverArgs, CC1Args, concat(SysRoot, "/usr/local/include"));
590590
// TOOL_INCLUDE_DIR
591591
AddMultilibIncludeArgs(DriverArgs, CC1Args);
592592

@@ -607,19 +607,20 @@ void Linux::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
607607
// /usr/include.
608608
std::string MultiarchIncludeDir = getMultiarchTriple(D, getTriple(), SysRoot);
609609
if (!MultiarchIncludeDir.empty() &&
610-
D.getVFS().exists(SysRoot + "/usr/include/" + MultiarchIncludeDir))
611-
addExternCSystemInclude(DriverArgs, CC1Args,
612-
SysRoot + "/usr/include/" + MultiarchIncludeDir);
610+
D.getVFS().exists(concat(SysRoot, "/usr/include", MultiarchIncludeDir)))
611+
addExternCSystemInclude(
612+
DriverArgs, CC1Args,
613+
concat(SysRoot, "/usr/include", MultiarchIncludeDir));
613614

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

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

622-
addExternCSystemInclude(DriverArgs, CC1Args, SysRoot + "/usr/include");
623+
addExternCSystemInclude(DriverArgs, CC1Args, concat(SysRoot, "/usr/include"));
623624

624625
if (!DriverArgs.hasArg(options::OPT_nobuiltininc) && getTriple().isMusl())
625626
addSystemInclude(DriverArgs, CC1Args, ResourceDirInclude);

clang/test/Driver/Inputs/basic_linux_libstdcxx_tree/usr/bin/.keep

Whitespace-only changes.

clang/test/Driver/Inputs/basic_linux_libstdcxx_tree/usr/lib/gcc/x86_64-unknown-linux-gnu/4.8/crtbegin.o

Whitespace-only changes.

clang/test/Driver/linux-header-search.cpp

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,22 @@
1616
// CHECK-BASIC-LIBCXX-SYSROOT: "-internal-isystem" "[[SYSROOT]]/usr/include/x86_64-unknown-linux-gnu/c++/v1"
1717
// CHECK-BASIC-LIBCXX-SYSROOT: "-internal-isystem" "[[SYSROOT]]/usr/include/c++/v1"
1818
// CHECK-BASIC-LIBCXX-SYSROOT: "-internal-isystem" "[[SYSROOT]]/usr/local/include"
19+
20+
// Test include paths when the sysroot path ends with `/`.
21+
// RUN: %clang -### %s -fsyntax-only 2>&1 \
22+
// RUN: --target=x86_64-unknown-linux-gnu \
23+
// RUN: -stdlib=libc++ \
24+
// RUN: -ccc-install-dir %S/Inputs/basic_linux_tree/usr/bin \
25+
// RUN: -resource-dir=%S/Inputs/resource_dir \
26+
// RUN: --sysroot=%S/Inputs/basic_linux_libcxx_tree/ \
27+
// RUN: --gcc-toolchain="" \
28+
// RUN: | FileCheck --check-prefix=CHECK-BASIC-LIBCXX-SYSROOT-SLASH %s
29+
// CHECK-BASIC-LIBCXX-SYSROOT-SLASH: "-cc1"
30+
// CHECK-BASIC-LIBCXX-SYSROOT-SLASH-SAME: "-isysroot" "[[SYSROOT:[^"]+/]]"
31+
// CHECK-BASIC-LIBCXX-SYSROOT-SLASH-SAME: "-internal-isystem" "[[SYSROOT]]usr/include/x86_64-unknown-linux-gnu/c++/v1"
32+
// CHECK-BASIC-LIBCXX-SYSROOT-SLASH-SAME: "-internal-isystem" "[[SYSROOT]]usr/include/c++/v1"
33+
// CHECK-BASIC-LIBCXX-SYSROOT-SLASH-SAME: "-internal-isystem" "[[SYSROOT]]usr/local/include"
34+
1935
// RUN: %clang -### %s -fsyntax-only 2>&1 \
2036
// RUN: --target=x86_64-unknown-linux-gnu \
2137
// RUN: -stdlib=libc++ \
@@ -56,7 +72,20 @@
5672
// CHECK-BASIC-LIBCXXV2-INSTALL: "-internal-isystem" "[[SYSROOT]]/usr/bin/../include/x86_64-unknown-linux-gnu/c++/v2"
5773
// CHECK-BASIC-LIBCXXV2-INSTALL: "-internal-isystem" "[[SYSROOT]]/usr/bin/../include/c++/v2"
5874
// CHECK-BASIC-LIBCXXV2-INSTALL: "-internal-isystem" "[[SYSROOT]]/usr/local/include"
59-
//
75+
76+
// Test Linux with libstdc++ when the sysroot path ends with `/`.
77+
// RUN: %clang -### %s -fsyntax-only 2>&1 \
78+
// RUN: --target=x86_64-unknown-linux-gnu \
79+
// RUN: -stdlib=libstdc++ \
80+
// RUN: -ccc-install-dir %S/Inputs/basic_linux_tree/usr/bin \
81+
// RUN: -resource-dir=%S/Inputs/resource_dir \
82+
// RUN: --sysroot=%S/Inputs/basic_linux_libstdcxx_tree/ \
83+
// RUN: --gcc-toolchain="" \
84+
// RUN: | FileCheck --check-prefix=CHECK-BASIC-LIBSTDCXX-SYSROOT-SLASH %s
85+
// CHECK-BASIC-LIBSTDCXX-SYSROOT-SLASH: "-cc1"
86+
// CHECK-BASIC-LIBSTDCXX-SYSROOT-SLASH-SAME: "-isysroot" "[[SYSROOT:[^"]+/]]"
87+
// 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"
88+
6089
// Test Linux with both libc++ and libstdc++ installed.
6190
// RUN: %clang -### %s -fsyntax-only 2>&1 \
6291
// RUN: --target=x86_64-unknown-linux-gnu \

0 commit comments

Comments
 (0)