Path: blob/main/contrib/llvm-project/clang/lib/Lex/InitHeaderSearch.cpp
35233 views
//===--- InitHeaderSearch.cpp - Initialize header search paths ------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file implements the InitHeaderSearch class.9//10//===----------------------------------------------------------------------===//1112#include "clang/Basic/DiagnosticFrontend.h"13#include "clang/Basic/FileManager.h"14#include "clang/Basic/LangOptions.h"15#include "clang/Config/config.h" // C_INCLUDE_DIRS16#include "clang/Lex/HeaderMap.h"17#include "clang/Lex/HeaderSearch.h"18#include "clang/Lex/HeaderSearchOptions.h"19#include "llvm/ADT/SmallPtrSet.h"20#include "llvm/ADT/SmallString.h"21#include "llvm/ADT/SmallVector.h"22#include "llvm/ADT/StringExtras.h"23#include "llvm/ADT/Twine.h"24#include "llvm/Support/ErrorHandling.h"25#include "llvm/Support/Path.h"26#include "llvm/Support/raw_ostream.h"27#include "llvm/TargetParser/Triple.h"28#include <optional>2930using namespace clang;31using namespace clang::frontend;3233namespace {34/// Holds information about a single DirectoryLookup object.35struct DirectoryLookupInfo {36IncludeDirGroup Group;37DirectoryLookup Lookup;38std::optional<unsigned> UserEntryIdx;3940DirectoryLookupInfo(IncludeDirGroup Group, DirectoryLookup Lookup,41std::optional<unsigned> UserEntryIdx)42: Group(Group), Lookup(Lookup), UserEntryIdx(UserEntryIdx) {}43};4445/// This class makes it easier to set the search paths of a HeaderSearch object.46/// InitHeaderSearch stores several search path lists internally, which can be47/// sent to a HeaderSearch object in one swoop.48class InitHeaderSearch {49std::vector<DirectoryLookupInfo> IncludePath;50std::vector<std::pair<std::string, bool> > SystemHeaderPrefixes;51HeaderSearch &Headers;52bool Verbose;53std::string IncludeSysroot;54bool HasSysroot;5556public:57InitHeaderSearch(HeaderSearch &HS, bool verbose, StringRef sysroot)58: Headers(HS), Verbose(verbose), IncludeSysroot(std::string(sysroot)),59HasSysroot(!(sysroot.empty() || sysroot == "/")) {}6061/// Add the specified path to the specified group list, prefixing the sysroot62/// if used.63/// Returns true if the path exists, false if it was ignored.64bool AddPath(const Twine &Path, IncludeDirGroup Group, bool isFramework,65std::optional<unsigned> UserEntryIdx = std::nullopt);6667/// Add the specified path to the specified group list, without performing any68/// sysroot remapping.69/// Returns true if the path exists, false if it was ignored.70bool AddUnmappedPath(const Twine &Path, IncludeDirGroup Group,71bool isFramework,72std::optional<unsigned> UserEntryIdx = std::nullopt);7374/// Add the specified prefix to the system header prefix list.75void AddSystemHeaderPrefix(StringRef Prefix, bool IsSystemHeader) {76SystemHeaderPrefixes.emplace_back(std::string(Prefix), IsSystemHeader);77}7879/// Add the necessary paths to support a MinGW libstdc++.80void AddMinGWCPlusPlusIncludePaths(StringRef Base,81StringRef Arch,82StringRef Version);8384/// Add paths that should always be searched.85void AddDefaultCIncludePaths(const llvm::Triple &triple,86const HeaderSearchOptions &HSOpts);8788/// Add paths that should be searched when compiling c++.89void AddDefaultCPlusPlusIncludePaths(const LangOptions &LangOpts,90const llvm::Triple &triple,91const HeaderSearchOptions &HSOpts);9293/// Returns true iff AddDefaultIncludePaths should do anything. If this94/// returns false, include paths should instead be handled in the driver.95bool ShouldAddDefaultIncludePaths(const llvm::Triple &triple);9697/// Adds the default system include paths so that e.g. stdio.h is found.98void AddDefaultIncludePaths(const LangOptions &Lang,99const llvm::Triple &triple,100const HeaderSearchOptions &HSOpts);101102/// Merges all search path lists into one list and send it to HeaderSearch.103void Realize(const LangOptions &Lang);104};105106} // end anonymous namespace.107108static bool CanPrefixSysroot(StringRef Path) {109#if defined(_WIN32)110return !Path.empty() && llvm::sys::path::is_separator(Path[0]);111#else112return llvm::sys::path::is_absolute(Path);113#endif114}115116bool InitHeaderSearch::AddPath(const Twine &Path, IncludeDirGroup Group,117bool isFramework,118std::optional<unsigned> UserEntryIdx) {119// Add the path with sysroot prepended, if desired and this is a system header120// group.121if (HasSysroot) {122SmallString<256> MappedPathStorage;123StringRef MappedPathStr = Path.toStringRef(MappedPathStorage);124if (CanPrefixSysroot(MappedPathStr)) {125return AddUnmappedPath(IncludeSysroot + Path, Group, isFramework,126UserEntryIdx);127}128}129130return AddUnmappedPath(Path, Group, isFramework, UserEntryIdx);131}132133bool InitHeaderSearch::AddUnmappedPath(const Twine &Path, IncludeDirGroup Group,134bool isFramework,135std::optional<unsigned> UserEntryIdx) {136assert(!Path.isTriviallyEmpty() && "can't handle empty path here");137138FileManager &FM = Headers.getFileMgr();139SmallString<256> MappedPathStorage;140StringRef MappedPathStr = Path.toStringRef(MappedPathStorage);141142// If use system headers while cross-compiling, emit the warning.143if (HasSysroot && (MappedPathStr.starts_with("/usr/include") ||144MappedPathStr.starts_with("/usr/local/include"))) {145Headers.getDiags().Report(diag::warn_poison_system_directories)146<< MappedPathStr;147}148149// Compute the DirectoryLookup type.150SrcMgr::CharacteristicKind Type;151if (Group == Quoted || Group == Angled || Group == IndexHeaderMap) {152Type = SrcMgr::C_User;153} else if (Group == ExternCSystem) {154Type = SrcMgr::C_ExternCSystem;155} else {156Type = SrcMgr::C_System;157}158159// If the directory exists, add it.160if (auto DE = FM.getOptionalDirectoryRef(MappedPathStr)) {161IncludePath.emplace_back(Group, DirectoryLookup(*DE, Type, isFramework),162UserEntryIdx);163return true;164}165166// Check to see if this is an apple-style headermap (which are not allowed to167// be frameworks).168if (!isFramework) {169if (auto FE = FM.getOptionalFileRef(MappedPathStr)) {170if (const HeaderMap *HM = Headers.CreateHeaderMap(*FE)) {171// It is a headermap, add it to the search path.172IncludePath.emplace_back(173Group, DirectoryLookup(HM, Type, Group == IndexHeaderMap),174UserEntryIdx);175return true;176}177}178}179180if (Verbose)181llvm::errs() << "ignoring nonexistent directory \""182<< MappedPathStr << "\"\n";183return false;184}185186void InitHeaderSearch::AddMinGWCPlusPlusIncludePaths(StringRef Base,187StringRef Arch,188StringRef Version) {189AddPath(Base + "/" + Arch + "/" + Version + "/include/c++",190CXXSystem, false);191AddPath(Base + "/" + Arch + "/" + Version + "/include/c++/" + Arch,192CXXSystem, false);193AddPath(Base + "/" + Arch + "/" + Version + "/include/c++/backward",194CXXSystem, false);195}196197void InitHeaderSearch::AddDefaultCIncludePaths(const llvm::Triple &triple,198const HeaderSearchOptions &HSOpts) {199if (!ShouldAddDefaultIncludePaths(triple))200llvm_unreachable("Include management is handled in the driver.");201202llvm::Triple::OSType os = triple.getOS();203204if (HSOpts.UseStandardSystemIncludes) {205switch (os) {206case llvm::Triple::Win32:207if (triple.getEnvironment() != llvm::Triple::Cygnus)208break;209[[fallthrough]];210default:211// FIXME: temporary hack: hard-coded paths.212AddPath("/usr/local/include", System, false);213break;214}215}216217// Builtin includes use #include_next directives and should be positioned218// just prior C include dirs.219if (HSOpts.UseBuiltinIncludes) {220// Ignore the sys root, we *always* look for clang headers relative to221// supplied path.222SmallString<128> P = StringRef(HSOpts.ResourceDir);223llvm::sys::path::append(P, "include");224AddUnmappedPath(P, ExternCSystem, false);225}226227// All remaining additions are for system include directories, early exit if228// we aren't using them.229if (!HSOpts.UseStandardSystemIncludes)230return;231232// Add dirs specified via 'configure --with-c-include-dirs'.233StringRef CIncludeDirs(C_INCLUDE_DIRS);234if (CIncludeDirs != "") {235SmallVector<StringRef, 5> dirs;236CIncludeDirs.split(dirs, ":");237for (StringRef dir : dirs)238AddPath(dir, ExternCSystem, false);239return;240}241242switch (os) {243case llvm::Triple::Win32:244switch (triple.getEnvironment()) {245default: llvm_unreachable("Include management is handled in the driver.");246case llvm::Triple::Cygnus:247AddPath("/usr/include/w32api", System, false);248break;249case llvm::Triple::GNU:250break;251}252break;253default:254break;255}256257AddPath("/usr/include", ExternCSystem, false);258}259260void InitHeaderSearch::AddDefaultCPlusPlusIncludePaths(261const LangOptions &LangOpts, const llvm::Triple &triple,262const HeaderSearchOptions &HSOpts) {263if (!ShouldAddDefaultIncludePaths(triple))264llvm_unreachable("Include management is handled in the driver.");265266// FIXME: temporary hack: hard-coded paths.267llvm::Triple::OSType os = triple.getOS();268switch (os) {269case llvm::Triple::Win32:270switch (triple.getEnvironment()) {271default: llvm_unreachable("Include management is handled in the driver.");272case llvm::Triple::Cygnus:273// Cygwin-1.7274AddMinGWCPlusPlusIncludePaths("/usr/lib/gcc", "i686-pc-cygwin", "4.7.3");275AddMinGWCPlusPlusIncludePaths("/usr/lib/gcc", "i686-pc-cygwin", "4.5.3");276AddMinGWCPlusPlusIncludePaths("/usr/lib/gcc", "i686-pc-cygwin", "4.3.4");277// g++-4 / Cygwin-1.5278AddMinGWCPlusPlusIncludePaths("/usr/lib/gcc", "i686-pc-cygwin", "4.3.2");279break;280}281break;282default:283break;284}285}286287bool InitHeaderSearch::ShouldAddDefaultIncludePaths(288const llvm::Triple &triple) {289switch (triple.getOS()) {290case llvm::Triple::AIX:291case llvm::Triple::DragonFly:292case llvm::Triple::ELFIAMCU:293case llvm::Triple::Emscripten:294case llvm::Triple::FreeBSD:295case llvm::Triple::Fuchsia:296case llvm::Triple::Haiku:297case llvm::Triple::Hurd:298case llvm::Triple::Linux:299case llvm::Triple::LiteOS:300case llvm::Triple::NaCl:301case llvm::Triple::NetBSD:302case llvm::Triple::OpenBSD:303case llvm::Triple::PS4:304case llvm::Triple::PS5:305case llvm::Triple::RTEMS:306case llvm::Triple::Solaris:307case llvm::Triple::WASI:308case llvm::Triple::ZOS:309return false;310311case llvm::Triple::Win32:312if (triple.getEnvironment() != llvm::Triple::Cygnus ||313triple.isOSBinFormatMachO())314return false;315break;316317case llvm::Triple::UnknownOS:318if (triple.isWasm())319return false;320break;321322default:323break;324}325326return true; // Everything else uses AddDefaultIncludePaths().327}328329void InitHeaderSearch::AddDefaultIncludePaths(330const LangOptions &Lang, const llvm::Triple &triple,331const HeaderSearchOptions &HSOpts) {332// NB: This code path is going away. All of the logic is moving into the333// driver which has the information necessary to do target-specific334// selections of default include paths. Each target which moves there will be335// exempted from this logic in ShouldAddDefaultIncludePaths() until we can336// delete the entire pile of code.337if (!ShouldAddDefaultIncludePaths(triple))338return;339340// NOTE: some additional header search logic is handled in the driver for341// Darwin.342if (triple.isOSDarwin()) {343if (HSOpts.UseStandardSystemIncludes) {344// Add the default framework include paths on Darwin.345if (triple.isDriverKit()) {346AddPath("/System/DriverKit/System/Library/Frameworks", System, true);347} else {348AddPath("/System/Library/Frameworks", System, true);349AddPath("/Library/Frameworks", System, true);350}351}352return;353}354355if (Lang.CPlusPlus && !Lang.AsmPreprocessor &&356HSOpts.UseStandardCXXIncludes && HSOpts.UseStandardSystemIncludes) {357if (HSOpts.UseLibcxx) {358AddPath("/usr/include/c++/v1", CXXSystem, false);359} else {360AddDefaultCPlusPlusIncludePaths(Lang, triple, HSOpts);361}362}363364AddDefaultCIncludePaths(triple, HSOpts);365}366367/// If there are duplicate directory entries in the specified search list,368/// remove the later (dead) ones. Returns the number of non-system headers369/// removed, which is used to update NumAngled.370static unsigned RemoveDuplicates(std::vector<DirectoryLookupInfo> &SearchList,371unsigned First, bool Verbose) {372llvm::SmallPtrSet<const DirectoryEntry *, 8> SeenDirs;373llvm::SmallPtrSet<const DirectoryEntry *, 8> SeenFrameworkDirs;374llvm::SmallPtrSet<const HeaderMap *, 8> SeenHeaderMaps;375unsigned NonSystemRemoved = 0;376for (unsigned i = First; i != SearchList.size(); ++i) {377unsigned DirToRemove = i;378379const DirectoryLookup &CurEntry = SearchList[i].Lookup;380381if (CurEntry.isNormalDir()) {382// If this isn't the first time we've seen this dir, remove it.383if (SeenDirs.insert(CurEntry.getDir()).second)384continue;385} else if (CurEntry.isFramework()) {386// If this isn't the first time we've seen this framework dir, remove it.387if (SeenFrameworkDirs.insert(CurEntry.getFrameworkDir()).second)388continue;389} else {390assert(CurEntry.isHeaderMap() && "Not a headermap or normal dir?");391// If this isn't the first time we've seen this headermap, remove it.392if (SeenHeaderMaps.insert(CurEntry.getHeaderMap()).second)393continue;394}395396// If we have a normal #include dir/framework/headermap that is shadowed397// later in the chain by a system include location, we actually want to398// ignore the user's request and drop the user dir... keeping the system399// dir. This is weird, but required to emulate GCC's search path correctly.400//401// Since dupes of system dirs are rare, just rescan to find the original402// that we're nuking instead of using a DenseMap.403if (CurEntry.getDirCharacteristic() != SrcMgr::C_User) {404// Find the dir that this is the same of.405unsigned FirstDir;406for (FirstDir = First;; ++FirstDir) {407assert(FirstDir != i && "Didn't find dupe?");408409const DirectoryLookup &SearchEntry = SearchList[FirstDir].Lookup;410411// If these are different lookup types, then they can't be the dupe.412if (SearchEntry.getLookupType() != CurEntry.getLookupType())413continue;414415bool isSame;416if (CurEntry.isNormalDir())417isSame = SearchEntry.getDir() == CurEntry.getDir();418else if (CurEntry.isFramework())419isSame = SearchEntry.getFrameworkDir() == CurEntry.getFrameworkDir();420else {421assert(CurEntry.isHeaderMap() && "Not a headermap or normal dir?");422isSame = SearchEntry.getHeaderMap() == CurEntry.getHeaderMap();423}424425if (isSame)426break;427}428429// If the first dir in the search path is a non-system dir, zap it430// instead of the system one.431if (SearchList[FirstDir].Lookup.getDirCharacteristic() == SrcMgr::C_User)432DirToRemove = FirstDir;433}434435if (Verbose) {436llvm::errs() << "ignoring duplicate directory \""437<< CurEntry.getName() << "\"\n";438if (DirToRemove != i)439llvm::errs() << " as it is a non-system directory that duplicates "440<< "a system directory\n";441}442if (DirToRemove != i)443++NonSystemRemoved;444445// This is reached if the current entry is a duplicate. Remove the446// DirToRemove (usually the current dir).447SearchList.erase(SearchList.begin()+DirToRemove);448--i;449}450return NonSystemRemoved;451}452453/// Extract DirectoryLookups from DirectoryLookupInfos.454static std::vector<DirectoryLookup>455extractLookups(const std::vector<DirectoryLookupInfo> &Infos) {456std::vector<DirectoryLookup> Lookups;457Lookups.reserve(Infos.size());458llvm::transform(Infos, std::back_inserter(Lookups),459[](const DirectoryLookupInfo &Info) { return Info.Lookup; });460return Lookups;461}462463/// Collect the mapping between indices of DirectoryLookups and UserEntries.464static llvm::DenseMap<unsigned, unsigned>465mapToUserEntries(const std::vector<DirectoryLookupInfo> &Infos) {466llvm::DenseMap<unsigned, unsigned> LookupsToUserEntries;467for (unsigned I = 0, E = Infos.size(); I < E; ++I) {468// Check whether this DirectoryLookup maps to a HeaderSearch::UserEntry.469if (Infos[I].UserEntryIdx)470LookupsToUserEntries.insert({I, *Infos[I].UserEntryIdx});471}472return LookupsToUserEntries;473}474475void InitHeaderSearch::Realize(const LangOptions &Lang) {476// Concatenate ANGLE+SYSTEM+AFTER chains together into SearchList.477std::vector<DirectoryLookupInfo> SearchList;478SearchList.reserve(IncludePath.size());479480// Quoted arguments go first.481for (auto &Include : IncludePath)482if (Include.Group == Quoted)483SearchList.push_back(Include);484485// Deduplicate and remember index.486RemoveDuplicates(SearchList, 0, Verbose);487unsigned NumQuoted = SearchList.size();488489for (auto &Include : IncludePath)490if (Include.Group == Angled || Include.Group == IndexHeaderMap)491SearchList.push_back(Include);492493RemoveDuplicates(SearchList, NumQuoted, Verbose);494unsigned NumAngled = SearchList.size();495496for (auto &Include : IncludePath)497if (Include.Group == System || Include.Group == ExternCSystem ||498(!Lang.ObjC && !Lang.CPlusPlus && Include.Group == CSystem) ||499(/*FIXME !Lang.ObjC && */ Lang.CPlusPlus &&500Include.Group == CXXSystem) ||501(Lang.ObjC && !Lang.CPlusPlus && Include.Group == ObjCSystem) ||502(Lang.ObjC && Lang.CPlusPlus && Include.Group == ObjCXXSystem))503SearchList.push_back(Include);504505for (auto &Include : IncludePath)506if (Include.Group == After)507SearchList.push_back(Include);508509// Remove duplicates across both the Angled and System directories. GCC does510// this and failing to remove duplicates across these two groups breaks511// #include_next.512unsigned NonSystemRemoved = RemoveDuplicates(SearchList, NumQuoted, Verbose);513NumAngled -= NonSystemRemoved;514515Headers.SetSearchPaths(extractLookups(SearchList), NumQuoted, NumAngled,516mapToUserEntries(SearchList));517518Headers.SetSystemHeaderPrefixes(SystemHeaderPrefixes);519520// If verbose, print the list of directories that will be searched.521if (Verbose) {522llvm::errs() << "#include \"...\" search starts here:\n";523for (unsigned i = 0, e = SearchList.size(); i != e; ++i) {524if (i == NumQuoted)525llvm::errs() << "#include <...> search starts here:\n";526StringRef Name = SearchList[i].Lookup.getName();527const char *Suffix;528if (SearchList[i].Lookup.isNormalDir())529Suffix = "";530else if (SearchList[i].Lookup.isFramework())531Suffix = " (framework directory)";532else {533assert(SearchList[i].Lookup.isHeaderMap() && "Unknown DirectoryLookup");534Suffix = " (headermap)";535}536llvm::errs() << " " << Name << Suffix << "\n";537}538llvm::errs() << "End of search list.\n";539}540}541542void clang::ApplyHeaderSearchOptions(HeaderSearch &HS,543const HeaderSearchOptions &HSOpts,544const LangOptions &Lang,545const llvm::Triple &Triple) {546InitHeaderSearch Init(HS, HSOpts.Verbose, HSOpts.Sysroot);547548// Add the user defined entries.549for (unsigned i = 0, e = HSOpts.UserEntries.size(); i != e; ++i) {550const HeaderSearchOptions::Entry &E = HSOpts.UserEntries[i];551if (E.IgnoreSysRoot) {552Init.AddUnmappedPath(E.Path, E.Group, E.IsFramework, i);553} else {554Init.AddPath(E.Path, E.Group, E.IsFramework, i);555}556}557558Init.AddDefaultIncludePaths(Lang, Triple, HSOpts);559560for (unsigned i = 0, e = HSOpts.SystemHeaderPrefixes.size(); i != e; ++i)561Init.AddSystemHeaderPrefix(HSOpts.SystemHeaderPrefixes[i].Prefix,562HSOpts.SystemHeaderPrefixes[i].IsSystemHeader);563564if (HSOpts.UseBuiltinIncludes) {565// Set up the builtin include directory in the module map.566SmallString<128> P = StringRef(HSOpts.ResourceDir);567llvm::sys::path::append(P, "include");568if (auto Dir = HS.getFileMgr().getOptionalDirectoryRef(P))569HS.getModuleMap().setBuiltinIncludeDir(*Dir);570}571572Init.Realize(Lang);573}574575576