Description
Clang has multiple flags for controlling the include path. From the documentation:
-nostdinc
Do not search the standard system directories or compiler builtin directories for include files.
-nostdlibinc
Do not search the standard system directories for include files, but do search compiler builtin include directories.
-nobuiltininc
Do not search clang’s builtin directory for include files.
So in effect, -nostdinc
implies -nostdlibinc
and -nobuiltininc
. But to control the standard system include dir and the builtins include dir separately, we have -nostdlibinc
and -nobuiltininc
to control those two kinds of include dirs separately.
You can see the logic here for the RISCV toolchain:
void RISCVToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
ArgStringList &CC1Args) const {
if (DriverArgs.hasArg(options::OPT_nostdinc))
return;
if (!DriverArgs.hasArg(options::OPT_nobuiltininc)) {
SmallString<128> Dir(getDriver().ResourceDir);
llvm::sys::path::append(Dir, "include");
addSystemInclude(DriverArgs, CC1Args, Dir.str());
}
if (!DriverArgs.hasArg(options::OPT_nostdlibinc)) {
SmallString<128> Dir(computeSysRoot());
llvm::sys::path::append(Dir, "include");
addSystemInclude(DriverArgs, CC1Args, Dir.str());
}
}
For BareMetal it's similar, with the addition of multilib:
void BareMetal::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
ArgStringList &CC1Args) const {
if (DriverArgs.hasArg(options::OPT_nostdinc))
return;
if (!DriverArgs.hasArg(options::OPT_nobuiltininc)) {
SmallString<128> Dir(getDriver().ResourceDir);
llvm::sys::path::append(Dir, "include");
addSystemInclude(DriverArgs, CC1Args, Dir.str());
}
if (!DriverArgs.hasArg(options::OPT_nostdlibinc)) {
const SmallString<128> SysRoot(computeSysRoot());
if (!SysRoot.empty()) {
for (const Multilib &M : getOrderedMultilibs()) {
SmallString<128> Dir(SysRoot);
llvm::sys::path::append(Dir, M.includeSuffix());
llvm::sys::path::append(Dir, "include");
addSystemInclude(DriverArgs, CC1Args, Dir.str());
}
}
}
}
Unfortunately, it seems that Xtensa ignores the builtins include directory unless a GCC installation has been found. In fact, if the -nostdlibinc
flag is passed, it exits early without adding the builtins directory at all!
void XtensaToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
ArgStringList &CC1Args) const {
if (DriverArgs.hasArg(clang::driver::options::OPT_nostdinc) ||
DriverArgs.hasArg(options::OPT_nostdlibinc))
return;
if (!getDriver().SysRoot.empty()) {
SmallString<128> Dir(getDriver().SysRoot);
llvm::sys::path::append(Dir, "include");
addSystemInclude(DriverArgs, CC1Args, Dir.str());
} else if (GCCInstallation.isValid()) {
SmallString<128> Path1(getDriver().ResourceDir);
llvm::sys::path::append(Path1, "include");
SmallString<128> Path2(GCCToolchainDir);
llvm::sys::path::append(Path2, GCCToolchainName, "sys-include");
SmallString<128> Path3(GCCToolchainDir);
llvm::sys::path::append(Path3, GCCToolchainName, "include");
const StringRef Paths[] = {Path1, Path2, Path3};
addSystemIncludes(DriverArgs, CC1Args, Paths);
} else {
SmallString<128> Dir(computeSysRoot());
llvm::sys::path::append(Dir, "include");
addSystemInclude(DriverArgs, CC1Args, Dir.str());
}
}
This makes no sense to me, since the resources directory is specific to Clang. On Linux, it's the /usr/lib/clang/<version>/include/
directory.
In TinyGo, we're not using a GCC installation. We use picolibc as the libc, and otherwise use LLVM native tools. Therefore, there is no GCC installation. To better control this, we pass -nostdlibinc
to ignore the system libc, with the expectation that the builtins directory is still added. Xtensa is the only platform where this is not the case.
Somehow this still works when using Clang as-is (hence why I don't have an easy reproducer) because Clang seems to add this directory again somewhere else, but when I use it with libclang and specify the resource directory manually, the builtins directory doesn't get added.