Skip to content

Commit c69436e

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 6c0442b commit c69436e

File tree

7 files changed

+75
-34
lines changed

7 files changed

+75
-34
lines changed

clang/include/clang/Driver/ToolChain.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,9 @@ class ToolChain {
211211
static void addSystemIncludes(const llvm::opt::ArgList &DriverArgs,
212212
llvm::opt::ArgStringList &CC1Args,
213213
ArrayRef<StringRef> Paths);
214+
215+
static std::string concat(StringRef Path, const Twine &A, const Twine &B = "",
216+
const Twine &C = "", const Twine &D = "");
214217
///@}
215218

216219
public:

clang/lib/Driver/ToolChain.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -876,6 +876,14 @@ void ToolChain::addExternCSystemIncludeIfExists(const ArgList &DriverArgs,
876876
}
877877
}
878878

879+
/*static*/ std::string ToolChain::concat(StringRef Path, const Twine &A,
880+
const Twine &B, const Twine &C,
881+
const Twine &D) {
882+
SmallString<128> Result(Path);
883+
llvm::sys::path::append(Result, llvm::sys::path::Style::posix, A, B, C, D);
884+
return std::string(Result);
885+
}
886+
879887
std::string ToolChain::detectLibcxxVersion(StringRef IncludePath) const {
880888
std::error_code EC;
881889
int MaxVersion = 0;

clang/lib/Driver/ToolChains/Gnu.cpp

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1957,7 +1957,7 @@ void Generic_GCC::GCCInstallationDetector::init(
19571957
if (!VFS.exists(Prefix))
19581958
continue;
19591959
for (StringRef Suffix : CandidateLibDirs) {
1960-
const std::string LibDir = Prefix + Suffix.str();
1960+
const std::string LibDir = concat(Prefix, Suffix);
19611961
if (!VFS.exists(LibDir))
19621962
continue;
19631963
// Maybe filter out <libdir>/gcc and <libdir>/gcc-cross.
@@ -2023,7 +2023,7 @@ void Generic_GCC::GCCInstallationDetector::AddDefaultGCCPrefixes(
20232023
// so we need to find those /usr/gcc/*/lib/gcc libdirs and go with
20242024
// /usr/gcc/<version> as a prefix.
20252025

2026-
std::string PrefixDir = SysRoot.str() + "/usr/gcc";
2026+
std::string PrefixDir = concat(SysRoot, "/usr/gcc");
20272027
std::error_code EC;
20282028
for (llvm::vfs::directory_iterator LI = D.getVFS().dir_begin(PrefixDir, EC),
20292029
LE;
@@ -2058,7 +2058,7 @@ void Generic_GCC::GCCInstallationDetector::AddDefaultGCCPrefixes(
20582058
Prefixes.push_back("/opt/rh/devtoolset-3/root/usr");
20592059
Prefixes.push_back("/opt/rh/devtoolset-2/root/usr");
20602060
}
2061-
Prefixes.push_back(SysRoot.str() + "/usr");
2061+
Prefixes.push_back(concat(SysRoot, "/usr"));
20622062
}
20632063

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

25772577
for (StringRef CandidateTriple : CandidateTriples) {
@@ -2590,8 +2590,8 @@ bool Generic_GCC::GCCInstallationDetector::ScanGentooGccConfig(
25902590
const llvm::Triple &TargetTriple, const ArgList &Args,
25912591
StringRef CandidateTriple, bool NeedsBiarchSuffix) {
25922592
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> File =
2593-
D.getVFS().getBufferForFile(D.SysRoot + GentooConfigDir + "/config-" +
2594-
CandidateTriple.str());
2593+
D.getVFS().getBufferForFile(concat(D.SysRoot, GentooConfigDir,
2594+
"/config-" + CandidateTriple.str()));
25952595
if (File) {
25962596
SmallVector<StringRef, 2> Lines;
25972597
File.get()->getBuffer().split(Lines, "\n");
@@ -2602,8 +2602,8 @@ bool Generic_GCC::GCCInstallationDetector::ScanGentooGccConfig(
26022602
continue;
26032603
// Process the config file pointed to by CURRENT.
26042604
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> ConfigFile =
2605-
D.getVFS().getBufferForFile(D.SysRoot + GentooConfigDir + "/" +
2606-
Line.str());
2605+
D.getVFS().getBufferForFile(
2606+
concat(D.SysRoot, GentooConfigDir, "/" + Line));
26072607
std::pair<StringRef, StringRef> ActiveVersion = Line.rsplit('-');
26082608
// List of paths to scan for libraries.
26092609
SmallVector<StringRef, 4> GentooScanPaths;
@@ -2636,7 +2636,7 @@ bool Generic_GCC::GCCInstallationDetector::ScanGentooGccConfig(
26362636

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

clang/lib/Driver/ToolChains/Linux.cpp

Lines changed: 23 additions & 22 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:
@@ -263,8 +263,8 @@ Linux::Linux(const Driver &D, const llvm::Triple &Triple, const ArgList &Args)
263263

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

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

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

283-
addPathIfExists(D, SysRoot + "/usr/lib/" + MultiarchTriple, Paths);
283+
addPathIfExists(D, concat(SysRoot, "/usr/lib", MultiarchTriple), Paths);
284284
// 64-bit OpenEmbedded sysroots may not have a /usr/lib dir. So they cannot
285285
// find /usr/lib64 as it is referenced as /usr/lib/../lib64. So we handle
286286
// this here.
287287
if (Triple.getVendor() == llvm::Triple::OpenEmbedded &&
288288
Triple.isArch64Bit())
289-
addPathIfExists(D, SysRoot + "/usr/" + OSLibDir, Paths);
289+
addPathIfExists(D, concat(SysRoot, "/usr", OSLibDir), Paths);
290290
else
291-
addPathIfExists(D, SysRoot + "/usr/lib/../" + OSLibDir, Paths);
291+
addPathIfExists(D, concat(SysRoot, "/usr/lib/..", OSLibDir), Paths);
292292
if (IsRISCV) {
293293
StringRef ABIName = tools::riscv::getRISCVABI(Args, Triple);
294-
addPathIfExists(D, SysRoot + "/" + OSLibDir + "/" + ABIName, Paths);
295-
addPathIfExists(D, SysRoot + "/usr/" + OSLibDir + "/" + ABIName, Paths);
294+
addPathIfExists(D, concat(SysRoot, "/", OSLibDir, ABIName), Paths);
295+
addPathIfExists(D, concat(SysRoot, "/usr", OSLibDir, ABIName), Paths);
296296
}
297297

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

313-
addPathIfExists(D, SysRoot + "/lib", Paths);
314-
addPathIfExists(D, SysRoot + "/usr/lib", Paths);
313+
addPathIfExists(D, concat(SysRoot, "/lib"), Paths);
314+
addPathIfExists(D, concat(SysRoot, "/usr/lib"), Paths);
315315
}
316316

317317
ToolChain::RuntimeLibType Linux::GetDefaultRuntimeLibType() const {
@@ -555,7 +555,7 @@ void Linux::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
555555
return;
556556

557557
// LOCAL_INCLUDE_DIR
558-
addSystemInclude(DriverArgs, CC1Args, SysRoot + "/usr/local/include");
558+
addSystemInclude(DriverArgs, CC1Args, concat(SysRoot, "/usr/local/include"));
559559
// TOOL_INCLUDE_DIR
560560
AddMultilibIncludeArgs(DriverArgs, CC1Args);
561561

@@ -576,19 +576,20 @@ void Linux::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
576576
// /usr/include.
577577
std::string MultiarchIncludeDir = getMultiarchTriple(D, getTriple(), SysRoot);
578578
if (!MultiarchIncludeDir.empty() &&
579-
D.getVFS().exists(SysRoot + "/usr/include/" + MultiarchIncludeDir))
580-
addExternCSystemInclude(DriverArgs, CC1Args,
581-
SysRoot + "/usr/include/" + MultiarchIncludeDir);
579+
D.getVFS().exists(concat(SysRoot, "/usr/include", MultiarchIncludeDir)))
580+
addExternCSystemInclude(
581+
DriverArgs, CC1Args,
582+
concat(SysRoot, "/usr/include", MultiarchIncludeDir));
582583

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

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

591-
addExternCSystemInclude(DriverArgs, CC1Args, SysRoot + "/usr/include");
592+
addExternCSystemInclude(DriverArgs, CC1Args, concat(SysRoot, "/usr/include"));
592593

593594
if (!DriverArgs.hasArg(options::OPT_nobuiltininc) && getTriple().isMusl())
594595
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)