480 lines · cpp
1//===--- InitHeaderSearch.cpp - Initialize header search paths ------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file implements the InitHeaderSearch class.10//11//===----------------------------------------------------------------------===//12 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/StringExtras.h"21#include "llvm/ADT/Twine.h"22#include "llvm/Support/ErrorHandling.h"23#include "llvm/Support/Path.h"24#include "llvm/Support/raw_ostream.h"25#include "llvm/TargetParser/Triple.h"26#include <optional>27 28using namespace clang;29using namespace clang::frontend;30 31namespace {32/// Holds information about a single DirectoryLookup object.33struct DirectoryLookupInfo {34 IncludeDirGroup Group;35 DirectoryLookup Lookup;36 std::optional<unsigned> UserEntryIdx;37 38 DirectoryLookupInfo(IncludeDirGroup Group, DirectoryLookup Lookup,39 std::optional<unsigned> UserEntryIdx)40 : Group(Group), Lookup(Lookup), UserEntryIdx(UserEntryIdx) {}41};42 43/// This class makes it easier to set the search paths of a HeaderSearch object.44/// InitHeaderSearch stores several search path lists internally, which can be45/// sent to a HeaderSearch object in one swoop.46class InitHeaderSearch {47 std::vector<DirectoryLookupInfo> IncludePath;48 std::vector<std::pair<std::string, bool> > SystemHeaderPrefixes;49 HeaderSearch &Headers;50 bool Verbose;51 std::string IncludeSysroot;52 bool HasSysroot;53 54public:55 InitHeaderSearch(HeaderSearch &HS, bool verbose, StringRef sysroot)56 : Headers(HS), Verbose(verbose), IncludeSysroot(std::string(sysroot)),57 HasSysroot(!(sysroot.empty() || sysroot == "/")) {}58 59 /// Add the specified path to the specified group list, prefixing the sysroot60 /// if used.61 /// Returns true if the path exists, false if it was ignored.62 bool AddPath(const Twine &Path, IncludeDirGroup Group, bool isFramework,63 std::optional<unsigned> UserEntryIdx = std::nullopt);64 65 /// Add the specified path to the specified group list, without performing any66 /// sysroot remapping.67 /// Returns true if the path exists, false if it was ignored.68 bool AddUnmappedPath(const Twine &Path, IncludeDirGroup Group,69 bool isFramework,70 std::optional<unsigned> UserEntryIdx = std::nullopt);71 72 /// Add the specified prefix to the system header prefix list.73 void AddSystemHeaderPrefix(StringRef Prefix, bool IsSystemHeader) {74 SystemHeaderPrefixes.emplace_back(std::string(Prefix), IsSystemHeader);75 }76 77 /// Add paths that should always be searched.78 void AddDefaultCIncludePaths(const llvm::Triple &triple,79 const HeaderSearchOptions &HSOpts);80 81 /// Returns true iff AddDefaultIncludePaths should do anything. If this82 /// returns false, include paths should instead be handled in the driver.83 bool ShouldAddDefaultIncludePaths(const llvm::Triple &triple);84 85 /// Adds the default system include paths so that e.g. stdio.h is found.86 void AddDefaultIncludePaths(const LangOptions &Lang,87 const llvm::Triple &triple,88 const HeaderSearchOptions &HSOpts);89 90 /// Merges all search path lists into one list and send it to HeaderSearch.91 void Realize(const LangOptions &Lang);92};93 94} // end anonymous namespace.95 96static bool CanPrefixSysroot(StringRef Path) {97#if defined(_WIN32)98 return !Path.empty() && llvm::sys::path::is_separator(Path[0]);99#else100 return llvm::sys::path::is_absolute(Path);101#endif102}103 104bool InitHeaderSearch::AddPath(const Twine &Path, IncludeDirGroup Group,105 bool isFramework,106 std::optional<unsigned> UserEntryIdx) {107 // Add the path with sysroot prepended, if desired and this is a system header108 // group.109 if (HasSysroot) {110 SmallString<256> MappedPathStorage;111 StringRef MappedPathStr = Path.toStringRef(MappedPathStorage);112 if (CanPrefixSysroot(MappedPathStr)) {113 return AddUnmappedPath(IncludeSysroot + Path, Group, isFramework,114 UserEntryIdx);115 }116 }117 118 return AddUnmappedPath(Path, Group, isFramework, UserEntryIdx);119}120 121bool InitHeaderSearch::AddUnmappedPath(const Twine &Path, IncludeDirGroup Group,122 bool isFramework,123 std::optional<unsigned> UserEntryIdx) {124 assert(!Path.isTriviallyEmpty() && "can't handle empty path here");125 126 FileManager &FM = Headers.getFileMgr();127 SmallString<256> MappedPathStorage;128 StringRef MappedPathStr = Path.toStringRef(MappedPathStorage);129 130 // If use system headers while cross-compiling, emit the warning.131 if (HasSysroot && (MappedPathStr.starts_with("/usr/include") ||132 MappedPathStr.starts_with("/usr/local/include"))) {133 Headers.getDiags().Report(diag::warn_poison_system_directories)134 << MappedPathStr;135 }136 137 // Compute the DirectoryLookup type.138 SrcMgr::CharacteristicKind Type;139 if (Group == Quoted || Group == Angled) {140 Type = SrcMgr::C_User;141 } else if (Group == ExternCSystem) {142 Type = SrcMgr::C_ExternCSystem;143 } else {144 Type = SrcMgr::C_System;145 }146 147 // If the directory exists, add it.148 if (auto DE = FM.getOptionalDirectoryRef(MappedPathStr)) {149 IncludePath.emplace_back(Group, DirectoryLookup(*DE, Type, isFramework),150 UserEntryIdx);151 return true;152 }153 154 // Check to see if this is an apple-style headermap (which are not allowed to155 // be frameworks).156 if (!isFramework) {157 if (auto FE = FM.getOptionalFileRef(MappedPathStr)) {158 if (const HeaderMap *HM = Headers.CreateHeaderMap(*FE)) {159 // It is a headermap, add it to the search path.160 IncludePath.emplace_back(Group, DirectoryLookup(HM, Type),161 UserEntryIdx);162 return true;163 }164 }165 }166 167 if (Verbose)168 llvm::errs() << "ignoring nonexistent directory \""169 << MappedPathStr << "\"\n";170 return false;171}172 173void InitHeaderSearch::AddDefaultCIncludePaths(const llvm::Triple &triple,174 const HeaderSearchOptions &HSOpts) {175 if (!ShouldAddDefaultIncludePaths(triple))176 llvm_unreachable("Include management is handled in the driver.");177 178 if (HSOpts.UseStandardSystemIncludes) {179 // FIXME: temporary hack: hard-coded paths.180 AddPath("/usr/local/include", System, false);181 }182 183 // Builtin includes use #include_next directives and should be positioned184 // just prior C include dirs.185 if (HSOpts.UseBuiltinIncludes) {186 // Ignore the sys root, we *always* look for clang headers relative to187 // supplied path.188 SmallString<128> P = StringRef(HSOpts.ResourceDir);189 llvm::sys::path::append(P, "include");190 AddUnmappedPath(P, ExternCSystem, false);191 }192 193 // All remaining additions are for system include directories, early exit if194 // we aren't using them.195 if (!HSOpts.UseStandardSystemIncludes)196 return;197 198 // Add dirs specified via 'configure --with-c-include-dirs'.199 StringRef CIncludeDirs(C_INCLUDE_DIRS);200 if (CIncludeDirs != "") {201 SmallVector<StringRef, 5> dirs;202 CIncludeDirs.split(dirs, ":");203 for (StringRef dir : dirs)204 AddPath(dir, ExternCSystem, false);205 return;206 }207 208 AddPath("/usr/include", ExternCSystem, false);209}210 211bool InitHeaderSearch::ShouldAddDefaultIncludePaths(212 const llvm::Triple &triple) {213 switch (triple.getOS()) {214 case llvm::Triple::AIX:215 case llvm::Triple::DragonFly:216 case llvm::Triple::ELFIAMCU:217 case llvm::Triple::Emscripten:218 case llvm::Triple::FreeBSD:219 case llvm::Triple::Fuchsia:220 case llvm::Triple::Haiku:221 case llvm::Triple::Hurd:222 case llvm::Triple::Linux:223 case llvm::Triple::LiteOS:224 case llvm::Triple::Managarm:225 case llvm::Triple::NetBSD:226 case llvm::Triple::OpenBSD:227 case llvm::Triple::PS4:228 case llvm::Triple::PS5:229 case llvm::Triple::RTEMS:230 case llvm::Triple::Solaris:231 case llvm::Triple::UEFI:232 case llvm::Triple::WASI:233 case llvm::Triple::Win32:234 case llvm::Triple::ZOS:235 return false;236 237 case llvm::Triple::UnknownOS:238 if (triple.isWasm() || triple.isAppleMachO())239 return false;240 break;241 242 default:243 break;244 }245 246 if (triple.isOSDarwin())247 return false;248 249 return true; // Everything else uses AddDefaultIncludePaths().250}251 252void InitHeaderSearch::AddDefaultIncludePaths(253 const LangOptions &Lang, const llvm::Triple &triple,254 const HeaderSearchOptions &HSOpts) {255 // NB: This code path is going away. All of the logic is moving into the256 // driver which has the information necessary to do target-specific257 // selections of default include paths. Each target which moves there will be258 // exempted from this logic in ShouldAddDefaultIncludePaths() until we can259 // delete the entire pile of code.260 if (!ShouldAddDefaultIncludePaths(triple))261 return;262 263 if (Lang.CPlusPlus && !Lang.AsmPreprocessor &&264 HSOpts.UseStandardCXXIncludes && HSOpts.UseStandardSystemIncludes) {265 if (HSOpts.UseLibcxx) {266 AddPath("/usr/include/c++/v1", CXXSystem, false);267 }268 }269 270 AddDefaultCIncludePaths(triple, HSOpts);271}272 273/// If there are duplicate directory entries in the specified search list,274/// remove the later (dead) ones. Returns the number of non-system headers275/// removed, which is used to update NumAngled.276static unsigned RemoveDuplicates(std::vector<DirectoryLookupInfo> &SearchList,277 unsigned First, bool Verbose) {278 llvm::SmallPtrSet<const DirectoryEntry *, 8> SeenDirs;279 llvm::SmallPtrSet<const DirectoryEntry *, 8> SeenFrameworkDirs;280 llvm::SmallPtrSet<const HeaderMap *, 8> SeenHeaderMaps;281 unsigned NonSystemRemoved = 0;282 for (unsigned i = First; i != SearchList.size(); ++i) {283 unsigned DirToRemove = i;284 285 const DirectoryLookup &CurEntry = SearchList[i].Lookup;286 287 if (CurEntry.isNormalDir()) {288 // If this isn't the first time we've seen this dir, remove it.289 if (SeenDirs.insert(CurEntry.getDir()).second)290 continue;291 } else if (CurEntry.isFramework()) {292 // If this isn't the first time we've seen this framework dir, remove it.293 if (SeenFrameworkDirs.insert(CurEntry.getFrameworkDir()).second)294 continue;295 } else {296 assert(CurEntry.isHeaderMap() && "Not a headermap or normal dir?");297 // If this isn't the first time we've seen this headermap, remove it.298 if (SeenHeaderMaps.insert(CurEntry.getHeaderMap()).second)299 continue;300 }301 302 // If we have a normal #include dir/framework/headermap that is shadowed303 // later in the chain by a system include location, we actually want to304 // ignore the user's request and drop the user dir... keeping the system305 // dir. This is weird, but required to emulate GCC's search path correctly.306 //307 // Since dupes of system dirs are rare, just rescan to find the original308 // that we're nuking instead of using a DenseMap.309 if (CurEntry.getDirCharacteristic() != SrcMgr::C_User) {310 // Find the dir that this is the same of.311 unsigned FirstDir;312 for (FirstDir = First;; ++FirstDir) {313 assert(FirstDir != i && "Didn't find dupe?");314 315 const DirectoryLookup &SearchEntry = SearchList[FirstDir].Lookup;316 317 // If these are different lookup types, then they can't be the dupe.318 if (SearchEntry.getLookupType() != CurEntry.getLookupType())319 continue;320 321 bool isSame;322 if (CurEntry.isNormalDir())323 isSame = SearchEntry.getDir() == CurEntry.getDir();324 else if (CurEntry.isFramework())325 isSame = SearchEntry.getFrameworkDir() == CurEntry.getFrameworkDir();326 else {327 assert(CurEntry.isHeaderMap() && "Not a headermap or normal dir?");328 isSame = SearchEntry.getHeaderMap() == CurEntry.getHeaderMap();329 }330 331 if (isSame)332 break;333 }334 335 // If the first dir in the search path is a non-system dir, zap it336 // instead of the system one.337 if (SearchList[FirstDir].Lookup.getDirCharacteristic() == SrcMgr::C_User)338 DirToRemove = FirstDir;339 }340 341 if (Verbose) {342 llvm::errs() << "ignoring duplicate directory \""343 << CurEntry.getName() << "\"\n";344 if (DirToRemove != i)345 llvm::errs() << " as it is a non-system directory that duplicates "346 << "a system directory\n";347 }348 if (DirToRemove != i)349 ++NonSystemRemoved;350 351 // This is reached if the current entry is a duplicate. Remove the352 // DirToRemove (usually the current dir).353 SearchList.erase(SearchList.begin()+DirToRemove);354 --i;355 }356 return NonSystemRemoved;357}358 359/// Extract DirectoryLookups from DirectoryLookupInfos.360static std::vector<DirectoryLookup>361extractLookups(const std::vector<DirectoryLookupInfo> &Infos) {362 std::vector<DirectoryLookup> Lookups;363 Lookups.reserve(Infos.size());364 llvm::transform(Infos, std::back_inserter(Lookups),365 [](const DirectoryLookupInfo &Info) { return Info.Lookup; });366 return Lookups;367}368 369/// Collect the mapping between indices of DirectoryLookups and UserEntries.370static llvm::DenseMap<unsigned, unsigned>371mapToUserEntries(const std::vector<DirectoryLookupInfo> &Infos) {372 llvm::DenseMap<unsigned, unsigned> LookupsToUserEntries;373 for (unsigned I = 0, E = Infos.size(); I < E; ++I) {374 // Check whether this DirectoryLookup maps to a HeaderSearch::UserEntry.375 if (Infos[I].UserEntryIdx)376 LookupsToUserEntries.insert({I, *Infos[I].UserEntryIdx});377 }378 return LookupsToUserEntries;379}380 381void InitHeaderSearch::Realize(const LangOptions &Lang) {382 // Concatenate ANGLE+SYSTEM+AFTER chains together into SearchList.383 std::vector<DirectoryLookupInfo> SearchList;384 SearchList.reserve(IncludePath.size());385 386 // Quoted arguments go first.387 for (auto &Include : IncludePath)388 if (Include.Group == Quoted)389 SearchList.push_back(Include);390 391 // Deduplicate and remember index.392 RemoveDuplicates(SearchList, 0, Verbose);393 unsigned NumQuoted = SearchList.size();394 395 for (auto &Include : IncludePath)396 if (Include.Group == Angled)397 SearchList.push_back(Include);398 399 RemoveDuplicates(SearchList, NumQuoted, Verbose);400 unsigned NumAngled = SearchList.size();401 402 for (auto &Include : IncludePath)403 if (Include.Group == System || Include.Group == ExternCSystem ||404 (!Lang.ObjC && !Lang.CPlusPlus && Include.Group == CSystem) ||405 (/*FIXME !Lang.ObjC && */ Lang.CPlusPlus &&406 Include.Group == CXXSystem) ||407 (Lang.ObjC && !Lang.CPlusPlus && Include.Group == ObjCSystem) ||408 (Lang.ObjC && Lang.CPlusPlus && Include.Group == ObjCXXSystem))409 SearchList.push_back(Include);410 411 for (auto &Include : IncludePath)412 if (Include.Group == After)413 SearchList.push_back(Include);414 415 // Remove duplicates across both the Angled and System directories. GCC does416 // this and failing to remove duplicates across these two groups breaks417 // #include_next.418 unsigned NonSystemRemoved = RemoveDuplicates(SearchList, NumQuoted, Verbose);419 NumAngled -= NonSystemRemoved;420 421 Headers.SetSearchPaths(extractLookups(SearchList), NumQuoted, NumAngled,422 mapToUserEntries(SearchList));423 424 Headers.SetSystemHeaderPrefixes(SystemHeaderPrefixes);425 426 // If verbose, print the list of directories that will be searched.427 if (Verbose) {428 llvm::errs() << "#include \"...\" search starts here:\n";429 for (unsigned i = 0, e = SearchList.size(); i != e; ++i) {430 if (i == NumQuoted)431 llvm::errs() << "#include <...> search starts here:\n";432 StringRef Name = SearchList[i].Lookup.getName();433 const char *Suffix;434 if (SearchList[i].Lookup.isNormalDir())435 Suffix = "";436 else if (SearchList[i].Lookup.isFramework())437 Suffix = " (framework directory)";438 else {439 assert(SearchList[i].Lookup.isHeaderMap() && "Unknown DirectoryLookup");440 Suffix = " (headermap)";441 }442 llvm::errs() << " " << Name << Suffix << "\n";443 }444 llvm::errs() << "End of search list.\n";445 }446}447 448void clang::ApplyHeaderSearchOptions(HeaderSearch &HS,449 const HeaderSearchOptions &HSOpts,450 const LangOptions &Lang,451 const llvm::Triple &Triple) {452 InitHeaderSearch Init(HS, HSOpts.Verbose, HSOpts.Sysroot);453 454 // Add the user defined entries.455 for (unsigned i = 0, e = HSOpts.UserEntries.size(); i != e; ++i) {456 const HeaderSearchOptions::Entry &E = HSOpts.UserEntries[i];457 if (E.IgnoreSysRoot) {458 Init.AddUnmappedPath(E.Path, E.Group, E.IsFramework, i);459 } else {460 Init.AddPath(E.Path, E.Group, E.IsFramework, i);461 }462 }463 464 Init.AddDefaultIncludePaths(Lang, Triple, HSOpts);465 466 for (unsigned i = 0, e = HSOpts.SystemHeaderPrefixes.size(); i != e; ++i)467 Init.AddSystemHeaderPrefix(HSOpts.SystemHeaderPrefixes[i].Prefix,468 HSOpts.SystemHeaderPrefixes[i].IsSystemHeader);469 470 if (HSOpts.UseBuiltinIncludes) {471 // Set up the builtin include directory in the module map.472 SmallString<128> P = StringRef(HSOpts.ResourceDir);473 llvm::sys::path::append(P, "include");474 if (auto Dir = HS.getFileMgr().getOptionalDirectoryRef(P))475 HS.getModuleMap().setBuiltinIncludeDir(*Dir);476 }477 478 Init.Realize(Lang);479}480