Skip to content

Commit 7a7dc5f

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 llvm#28283. Differential Revision: https://reviews.llvm.org/D126289
1 parent fc6c14f commit 7a7dc5f

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
@@ -213,6 +213,9 @@ class ToolChain {
213213
static void addSystemIncludes(const llvm::opt::ArgList &DriverArgs,
214214
llvm::opt::ArgStringList &CC1Args,
215215
ArrayRef<StringRef> Paths);
216+
217+
static std::string concat(StringRef Path, const Twine &A, const Twine &B = "",
218+
const Twine &C = "", const Twine &D = "");
216219
///@}
217220

218221
public:

clang/lib/Driver/ToolChain.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -905,6 +905,14 @@ void ToolChain::addExternCSystemIncludeIfExists(const ArgList &DriverArgs,
905905
}
906906
}
907907

908+
/*static*/ std::string ToolChain::concat(StringRef Path, const Twine &A,
909+
const Twine &B, const Twine &C,
910+
const Twine &D) {
911+
SmallString<128> Result(Path);
912+
llvm::sys::path::append(Result, llvm::sys::path::Style::posix, A, B, C, D);
913+
return std::string(Result);
914+
}
915+
908916
std::string ToolChain::detectLibcxxVersion(StringRef IncludePath) const {
909917
std::error_code EC;
910918
int MaxVersion = 0;

clang/lib/Driver/ToolChains/Gnu.cpp

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2021,7 +2021,7 @@ void Generic_GCC::GCCInstallationDetector::init(
20212021
if (!VFS.exists(Prefix))
20222022
continue;
20232023
for (StringRef Suffix : CandidateLibDirs) {
2024-
const std::string LibDir = Prefix + Suffix.str();
2024+
const std::string LibDir = concat(Prefix, Suffix);
20252025
if (!VFS.exists(LibDir))
20262026
continue;
20272027
// Maybe filter out <libdir>/gcc and <libdir>/gcc-cross.
@@ -2087,7 +2087,7 @@ void Generic_GCC::GCCInstallationDetector::AddDefaultGCCPrefixes(
20872087
// so we need to find those /usr/gcc/*/lib/gcc libdirs and go with
20882088
// /usr/gcc/<version> as a prefix.
20892089

2090-
std::string PrefixDir = SysRoot.str() + "/usr/gcc";
2090+
std::string PrefixDir = concat(SysRoot, "/usr/gcc");
20912091
std::error_code EC;
20922092
for (llvm::vfs::directory_iterator LI = D.getVFS().dir_begin(PrefixDir, EC),
20932093
LE;
@@ -2122,7 +2122,7 @@ void Generic_GCC::GCCInstallationDetector::AddDefaultGCCPrefixes(
21222122
Prefixes.push_back("/opt/rh/devtoolset-3/root/usr");
21232123
Prefixes.push_back("/opt/rh/devtoolset-2/root/usr");
21242124
}
2125-
Prefixes.push_back(SysRoot.str() + "/usr");
2125+
Prefixes.push_back(concat(SysRoot, "/usr"));
21262126
}
21272127

21282128
/*static*/ void Generic_GCC::GCCInstallationDetector::CollectLibDirsAndTriples(
@@ -2645,7 +2645,7 @@ bool Generic_GCC::GCCInstallationDetector::ScanGentooConfigs(
26452645
const llvm::Triple &TargetTriple, const ArgList &Args,
26462646
const SmallVectorImpl<StringRef> &CandidateTriples,
26472647
const SmallVectorImpl<StringRef> &CandidateBiarchTriples) {
2648-
if (!D.getVFS().exists(D.SysRoot + GentooConfigDir))
2648+
if (!D.getVFS().exists(concat(D.SysRoot, GentooConfigDir)))
26492649
return false;
26502650

26512651
for (StringRef CandidateTriple : CandidateTriples) {
@@ -2664,8 +2664,8 @@ bool Generic_GCC::GCCInstallationDetector::ScanGentooGccConfig(
26642664
const llvm::Triple &TargetTriple, const ArgList &Args,
26652665
StringRef CandidateTriple, bool NeedsBiarchSuffix) {
26662666
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> File =
2667-
D.getVFS().getBufferForFile(D.SysRoot + GentooConfigDir + "/config-" +
2668-
CandidateTriple.str());
2667+
D.getVFS().getBufferForFile(concat(D.SysRoot, GentooConfigDir,
2668+
"/config-" + CandidateTriple.str()));
26692669
if (File) {
26702670
SmallVector<StringRef, 2> Lines;
26712671
File.get()->getBuffer().split(Lines, "\n");
@@ -2676,8 +2676,8 @@ bool Generic_GCC::GCCInstallationDetector::ScanGentooGccConfig(
26762676
continue;
26772677
// Process the config file pointed to by CURRENT.
26782678
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> ConfigFile =
2679-
D.getVFS().getBufferForFile(D.SysRoot + GentooConfigDir + "/" +
2680-
Line.str());
2679+
D.getVFS().getBufferForFile(
2680+
concat(D.SysRoot, GentooConfigDir, "/" + Line));
26812681
std::pair<StringRef, StringRef> ActiveVersion = Line.rsplit('-');
26822682
// List of paths to scan for libraries.
26832683
SmallVector<StringRef, 4> GentooScanPaths;
@@ -2710,7 +2710,7 @@ bool Generic_GCC::GCCInstallationDetector::ScanGentooGccConfig(
27102710

27112711
// Scan all paths for GCC libraries.
27122712
for (const auto &GentooScanPath : GentooScanPaths) {
2713-
std::string GentooPath = D.SysRoot + std::string(GentooScanPath);
2713+
std::string GentooPath = concat(D.SysRoot, GentooScanPath);
27142714
if (D.getVFS().exists(GentooPath + "/crtbegin.o")) {
27152715
if (!ScanGCCForMultilibs(TargetTriple, Args, GentooPath,
27162716
NeedsBiarchSuffix))
@@ -3003,9 +3003,9 @@ Generic_GCC::addLibCxxIncludePaths(const llvm::opt::ArgList &DriverArgs,
30033003
// If this is a development, non-installed, clang, libcxx will
30043004
// not be found at ../include/c++ but it likely to be found at
30053005
// one of the following two locations:
3006-
if (AddIncludePath(SysRoot + "/usr/local/include"))
3006+
if (AddIncludePath(concat(SysRoot, "/usr/local/include")))
30073007
return;
3008-
if (AddIncludePath(SysRoot + "/usr/include"))
3008+
if (AddIncludePath(concat(SysRoot, "/usr/include")))
30093009
return;
30103010
}
30113011

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 -no-canonical-prefixes %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 -no-canonical-prefixes %s -### -fsyntax-only 2>&1 \
6291
// RUN: -target x86_64-unknown-linux-gnu \

0 commit comments

Comments
 (0)