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-exception 6 // 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_DIRS 16 #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 28 using namespace clang; 29 using namespace clang::frontend; 30 31 namespace { 32 /// Holds information about a single DirectoryLookup object. 33 struct 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 be 45 /// sent to a HeaderSearch object in one swoop. 46 class 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 54 public: 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 sysroot 60 /// 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 any 66 /// 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 the necessary paths to support a MinGW libstdc++. 78 void AddMinGWCPlusPlusIncludePaths(StringRef Base, 79 StringRef Arch, 80 StringRef Version); 81 82 /// Add paths that should always be searched. 83 void AddDefaultCIncludePaths(const llvm::Triple &triple, 84 const HeaderSearchOptions &HSOpts); 85 86 /// Add paths that should be searched when compiling c++. 87 void AddDefaultCPlusPlusIncludePaths(const LangOptions &LangOpts, 88 const llvm::Triple &triple, 89 const HeaderSearchOptions &HSOpts); 90 91 /// Returns true iff AddDefaultIncludePaths should do anything. If this 92 /// returns false, include paths should instead be handled in the driver. 93 bool ShouldAddDefaultIncludePaths(const llvm::Triple &triple); 94 95 /// Adds the default system include paths so that e.g. stdio.h is found. 96 void AddDefaultIncludePaths(const LangOptions &Lang, 97 const llvm::Triple &triple, 98 const HeaderSearchOptions &HSOpts); 99 100 /// Merges all search path lists into one list and send it to HeaderSearch. 101 void Realize(const LangOptions &Lang); 102 }; 103 104 } // end anonymous namespace. 105 106 static bool CanPrefixSysroot(StringRef Path) { 107 #if defined(_WIN32) 108 return !Path.empty() && llvm::sys::path::is_separator(Path[0]); 109 #else 110 return llvm::sys::path::is_absolute(Path); 111 #endif 112 } 113 114 bool InitHeaderSearch::AddPath(const Twine &Path, IncludeDirGroup Group, 115 bool isFramework, 116 std::optional<unsigned> UserEntryIdx) { 117 // Add the path with sysroot prepended, if desired and this is a system header 118 // group. 119 if (HasSysroot) { 120 SmallString<256> MappedPathStorage; 121 StringRef MappedPathStr = Path.toStringRef(MappedPathStorage); 122 if (CanPrefixSysroot(MappedPathStr)) { 123 return AddUnmappedPath(IncludeSysroot + Path, Group, isFramework, 124 UserEntryIdx); 125 } 126 } 127 128 return AddUnmappedPath(Path, Group, isFramework, UserEntryIdx); 129 } 130 131 bool InitHeaderSearch::AddUnmappedPath(const Twine &Path, IncludeDirGroup Group, 132 bool isFramework, 133 std::optional<unsigned> UserEntryIdx) { 134 assert(!Path.isTriviallyEmpty() && "can't handle empty path here"); 135 136 FileManager &FM = Headers.getFileMgr(); 137 SmallString<256> MappedPathStorage; 138 StringRef MappedPathStr = Path.toStringRef(MappedPathStorage); 139 140 // If use system headers while cross-compiling, emit the warning. 141 if (HasSysroot && (MappedPathStr.starts_with("/usr/include") || 142 MappedPathStr.starts_with("/usr/local/include"))) { 143 Headers.getDiags().Report(diag::warn_poison_system_directories) 144 << MappedPathStr; 145 } 146 147 // Compute the DirectoryLookup type. 148 SrcMgr::CharacteristicKind Type; 149 if (Group == Quoted || Group == Angled) { 150 Type = SrcMgr::C_User; 151 } else if (Group == ExternCSystem) { 152 Type = SrcMgr::C_ExternCSystem; 153 } else { 154 Type = SrcMgr::C_System; 155 } 156 157 // If the directory exists, add it. 158 if (auto DE = FM.getOptionalDirectoryRef(MappedPathStr)) { 159 IncludePath.emplace_back(Group, DirectoryLookup(*DE, Type, isFramework), 160 UserEntryIdx); 161 return true; 162 } 163 164 // Check to see if this is an apple-style headermap (which are not allowed to 165 // be frameworks). 166 if (!isFramework) { 167 if (auto FE = FM.getOptionalFileRef(MappedPathStr)) { 168 if (const HeaderMap *HM = Headers.CreateHeaderMap(*FE)) { 169 // It is a headermap, add it to the search path. 170 IncludePath.emplace_back(Group, DirectoryLookup(HM, Type), 171 UserEntryIdx); 172 return true; 173 } 174 } 175 } 176 177 if (Verbose) 178 llvm::errs() << "ignoring nonexistent directory \"" 179 << MappedPathStr << "\"\n"; 180 return false; 181 } 182 183 void InitHeaderSearch::AddMinGWCPlusPlusIncludePaths(StringRef Base, 184 StringRef Arch, 185 StringRef Version) { 186 AddPath(Base + "/" + Arch + "/" + Version + "/include/c++", 187 CXXSystem, false); 188 AddPath(Base + "/" + Arch + "/" + Version + "/include/c++/" + Arch, 189 CXXSystem, false); 190 AddPath(Base + "/" + Arch + "/" + Version + "/include/c++/backward", 191 CXXSystem, false); 192 } 193 194 void InitHeaderSearch::AddDefaultCIncludePaths(const llvm::Triple &triple, 195 const HeaderSearchOptions &HSOpts) { 196 if (!ShouldAddDefaultIncludePaths(triple)) 197 llvm_unreachable("Include management is handled in the driver."); 198 199 llvm::Triple::OSType os = triple.getOS(); 200 201 if (HSOpts.UseStandardSystemIncludes) { 202 switch (os) { 203 case llvm::Triple::Win32: 204 if (triple.getEnvironment() != llvm::Triple::Cygnus) 205 break; 206 [[fallthrough]]; 207 default: 208 // FIXME: temporary hack: hard-coded paths. 209 AddPath("/usr/local/include", System, false); 210 break; 211 } 212 } 213 214 // Builtin includes use #include_next directives and should be positioned 215 // just prior C include dirs. 216 if (HSOpts.UseBuiltinIncludes) { 217 // Ignore the sys root, we *always* look for clang headers relative to 218 // supplied path. 219 SmallString<128> P = StringRef(HSOpts.ResourceDir); 220 llvm::sys::path::append(P, "include"); 221 AddUnmappedPath(P, ExternCSystem, false); 222 } 223 224 // All remaining additions are for system include directories, early exit if 225 // we aren't using them. 226 if (!HSOpts.UseStandardSystemIncludes) 227 return; 228 229 // Add dirs specified via 'configure --with-c-include-dirs'. 230 StringRef CIncludeDirs(C_INCLUDE_DIRS); 231 if (CIncludeDirs != "") { 232 SmallVector<StringRef, 5> dirs; 233 CIncludeDirs.split(dirs, ":"); 234 for (StringRef dir : dirs) 235 AddPath(dir, ExternCSystem, false); 236 return; 237 } 238 239 switch (os) { 240 case llvm::Triple::Win32: 241 switch (triple.getEnvironment()) { 242 default: llvm_unreachable("Include management is handled in the driver."); 243 case llvm::Triple::Cygnus: 244 AddPath("/usr/include/w32api", System, false); 245 break; 246 case llvm::Triple::GNU: 247 break; 248 } 249 break; 250 default: 251 break; 252 } 253 254 AddPath("/usr/include", ExternCSystem, false); 255 } 256 257 void InitHeaderSearch::AddDefaultCPlusPlusIncludePaths( 258 const LangOptions &LangOpts, const llvm::Triple &triple, 259 const HeaderSearchOptions &HSOpts) { 260 if (!ShouldAddDefaultIncludePaths(triple)) 261 llvm_unreachable("Include management is handled in the driver."); 262 263 // FIXME: temporary hack: hard-coded paths. 264 llvm::Triple::OSType os = triple.getOS(); 265 switch (os) { 266 case llvm::Triple::Win32: 267 switch (triple.getEnvironment()) { 268 default: llvm_unreachable("Include management is handled in the driver."); 269 case llvm::Triple::Cygnus: 270 // Cygwin-1.7 271 AddMinGWCPlusPlusIncludePaths("/usr/lib/gcc", "i686-pc-cygwin", "4.7.3"); 272 AddMinGWCPlusPlusIncludePaths("/usr/lib/gcc", "i686-pc-cygwin", "4.5.3"); 273 AddMinGWCPlusPlusIncludePaths("/usr/lib/gcc", "i686-pc-cygwin", "4.3.4"); 274 // g++-4 / Cygwin-1.5 275 AddMinGWCPlusPlusIncludePaths("/usr/lib/gcc", "i686-pc-cygwin", "4.3.2"); 276 break; 277 } 278 break; 279 default: 280 break; 281 } 282 } 283 284 bool InitHeaderSearch::ShouldAddDefaultIncludePaths( 285 const llvm::Triple &triple) { 286 switch (triple.getOS()) { 287 case llvm::Triple::AIX: 288 case llvm::Triple::DragonFly: 289 case llvm::Triple::ELFIAMCU: 290 case llvm::Triple::Emscripten: 291 case llvm::Triple::FreeBSD: 292 case llvm::Triple::Fuchsia: 293 case llvm::Triple::Haiku: 294 case llvm::Triple::Hurd: 295 case llvm::Triple::Linux: 296 case llvm::Triple::LiteOS: 297 case llvm::Triple::NaCl: 298 case llvm::Triple::NetBSD: 299 case llvm::Triple::OpenBSD: 300 case llvm::Triple::PS4: 301 case llvm::Triple::PS5: 302 case llvm::Triple::RTEMS: 303 case llvm::Triple::Solaris: 304 case llvm::Triple::UEFI: 305 case llvm::Triple::WASI: 306 case llvm::Triple::ZOS: 307 return false; 308 309 case llvm::Triple::Win32: 310 if (triple.getEnvironment() != llvm::Triple::Cygnus || 311 triple.isOSBinFormatMachO()) 312 return false; 313 break; 314 315 case llvm::Triple::UnknownOS: 316 if (triple.isWasm() || triple.isAppleMachO()) 317 return false; 318 break; 319 320 default: 321 break; 322 } 323 324 return true; // Everything else uses AddDefaultIncludePaths(). 325 } 326 327 void InitHeaderSearch::AddDefaultIncludePaths( 328 const LangOptions &Lang, const llvm::Triple &triple, 329 const HeaderSearchOptions &HSOpts) { 330 // NB: This code path is going away. All of the logic is moving into the 331 // driver which has the information necessary to do target-specific 332 // selections of default include paths. Each target which moves there will be 333 // exempted from this logic in ShouldAddDefaultIncludePaths() until we can 334 // delete the entire pile of code. 335 if (!ShouldAddDefaultIncludePaths(triple)) 336 return; 337 338 // NOTE: some additional header search logic is handled in the driver for 339 // Darwin. 340 if (triple.isOSDarwin()) { 341 if (HSOpts.UseStandardSystemIncludes) { 342 // Add the default framework include paths on Darwin. 343 if (triple.isDriverKit()) { 344 AddPath("/System/DriverKit/System/Library/Frameworks", System, true); 345 } else { 346 AddPath("/System/Library/Frameworks", System, true); 347 AddPath("/System/Library/SubFrameworks", System, true); 348 AddPath("/Library/Frameworks", System, true); 349 } 350 } 351 return; 352 } 353 354 if (Lang.CPlusPlus && !Lang.AsmPreprocessor && 355 HSOpts.UseStandardCXXIncludes && HSOpts.UseStandardSystemIncludes) { 356 if (HSOpts.UseLibcxx) { 357 AddPath("/usr/include/c++/v1", CXXSystem, false); 358 } else { 359 AddDefaultCPlusPlusIncludePaths(Lang, triple, HSOpts); 360 } 361 } 362 363 AddDefaultCIncludePaths(triple, HSOpts); 364 } 365 366 /// If there are duplicate directory entries in the specified search list, 367 /// remove the later (dead) ones. Returns the number of non-system headers 368 /// removed, which is used to update NumAngled. 369 static unsigned RemoveDuplicates(std::vector<DirectoryLookupInfo> &SearchList, 370 unsigned First, bool Verbose) { 371 llvm::SmallPtrSet<const DirectoryEntry *, 8> SeenDirs; 372 llvm::SmallPtrSet<const DirectoryEntry *, 8> SeenFrameworkDirs; 373 llvm::SmallPtrSet<const HeaderMap *, 8> SeenHeaderMaps; 374 unsigned NonSystemRemoved = 0; 375 for (unsigned i = First; i != SearchList.size(); ++i) { 376 unsigned DirToRemove = i; 377 378 const DirectoryLookup &CurEntry = SearchList[i].Lookup; 379 380 if (CurEntry.isNormalDir()) { 381 // If this isn't the first time we've seen this dir, remove it. 382 if (SeenDirs.insert(CurEntry.getDir()).second) 383 continue; 384 } else if (CurEntry.isFramework()) { 385 // If this isn't the first time we've seen this framework dir, remove it. 386 if (SeenFrameworkDirs.insert(CurEntry.getFrameworkDir()).second) 387 continue; 388 } else { 389 assert(CurEntry.isHeaderMap() && "Not a headermap or normal dir?"); 390 // If this isn't the first time we've seen this headermap, remove it. 391 if (SeenHeaderMaps.insert(CurEntry.getHeaderMap()).second) 392 continue; 393 } 394 395 // If we have a normal #include dir/framework/headermap that is shadowed 396 // later in the chain by a system include location, we actually want to 397 // ignore the user's request and drop the user dir... keeping the system 398 // dir. This is weird, but required to emulate GCC's search path correctly. 399 // 400 // Since dupes of system dirs are rare, just rescan to find the original 401 // that we're nuking instead of using a DenseMap. 402 if (CurEntry.getDirCharacteristic() != SrcMgr::C_User) { 403 // Find the dir that this is the same of. 404 unsigned FirstDir; 405 for (FirstDir = First;; ++FirstDir) { 406 assert(FirstDir != i && "Didn't find dupe?"); 407 408 const DirectoryLookup &SearchEntry = SearchList[FirstDir].Lookup; 409 410 // If these are different lookup types, then they can't be the dupe. 411 if (SearchEntry.getLookupType() != CurEntry.getLookupType()) 412 continue; 413 414 bool isSame; 415 if (CurEntry.isNormalDir()) 416 isSame = SearchEntry.getDir() == CurEntry.getDir(); 417 else if (CurEntry.isFramework()) 418 isSame = SearchEntry.getFrameworkDir() == CurEntry.getFrameworkDir(); 419 else { 420 assert(CurEntry.isHeaderMap() && "Not a headermap or normal dir?"); 421 isSame = SearchEntry.getHeaderMap() == CurEntry.getHeaderMap(); 422 } 423 424 if (isSame) 425 break; 426 } 427 428 // If the first dir in the search path is a non-system dir, zap it 429 // instead of the system one. 430 if (SearchList[FirstDir].Lookup.getDirCharacteristic() == SrcMgr::C_User) 431 DirToRemove = FirstDir; 432 } 433 434 if (Verbose) { 435 llvm::errs() << "ignoring duplicate directory \"" 436 << CurEntry.getName() << "\"\n"; 437 if (DirToRemove != i) 438 llvm::errs() << " as it is a non-system directory that duplicates " 439 << "a system directory\n"; 440 } 441 if (DirToRemove != i) 442 ++NonSystemRemoved; 443 444 // This is reached if the current entry is a duplicate. Remove the 445 // DirToRemove (usually the current dir). 446 SearchList.erase(SearchList.begin()+DirToRemove); 447 --i; 448 } 449 return NonSystemRemoved; 450 } 451 452 /// Extract DirectoryLookups from DirectoryLookupInfos. 453 static std::vector<DirectoryLookup> 454 extractLookups(const std::vector<DirectoryLookupInfo> &Infos) { 455 std::vector<DirectoryLookup> Lookups; 456 Lookups.reserve(Infos.size()); 457 llvm::transform(Infos, std::back_inserter(Lookups), 458 [](const DirectoryLookupInfo &Info) { return Info.Lookup; }); 459 return Lookups; 460 } 461 462 /// Collect the mapping between indices of DirectoryLookups and UserEntries. 463 static llvm::DenseMap<unsigned, unsigned> 464 mapToUserEntries(const std::vector<DirectoryLookupInfo> &Infos) { 465 llvm::DenseMap<unsigned, unsigned> LookupsToUserEntries; 466 for (unsigned I = 0, E = Infos.size(); I < E; ++I) { 467 // Check whether this DirectoryLookup maps to a HeaderSearch::UserEntry. 468 if (Infos[I].UserEntryIdx) 469 LookupsToUserEntries.insert({I, *Infos[I].UserEntryIdx}); 470 } 471 return LookupsToUserEntries; 472 } 473 474 void InitHeaderSearch::Realize(const LangOptions &Lang) { 475 // Concatenate ANGLE+SYSTEM+AFTER chains together into SearchList. 476 std::vector<DirectoryLookupInfo> SearchList; 477 SearchList.reserve(IncludePath.size()); 478 479 // Quoted arguments go first. 480 for (auto &Include : IncludePath) 481 if (Include.Group == Quoted) 482 SearchList.push_back(Include); 483 484 // Deduplicate and remember index. 485 RemoveDuplicates(SearchList, 0, Verbose); 486 unsigned NumQuoted = SearchList.size(); 487 488 for (auto &Include : IncludePath) 489 if (Include.Group == Angled) 490 SearchList.push_back(Include); 491 492 RemoveDuplicates(SearchList, NumQuoted, Verbose); 493 unsigned NumAngled = SearchList.size(); 494 495 for (auto &Include : IncludePath) 496 if (Include.Group == System || Include.Group == ExternCSystem || 497 (!Lang.ObjC && !Lang.CPlusPlus && Include.Group == CSystem) || 498 (/*FIXME !Lang.ObjC && */ Lang.CPlusPlus && 499 Include.Group == CXXSystem) || 500 (Lang.ObjC && !Lang.CPlusPlus && Include.Group == ObjCSystem) || 501 (Lang.ObjC && Lang.CPlusPlus && Include.Group == ObjCXXSystem)) 502 SearchList.push_back(Include); 503 504 for (auto &Include : IncludePath) 505 if (Include.Group == After) 506 SearchList.push_back(Include); 507 508 // Remove duplicates across both the Angled and System directories. GCC does 509 // this and failing to remove duplicates across these two groups breaks 510 // #include_next. 511 unsigned NonSystemRemoved = RemoveDuplicates(SearchList, NumQuoted, Verbose); 512 NumAngled -= NonSystemRemoved; 513 514 Headers.SetSearchPaths(extractLookups(SearchList), NumQuoted, NumAngled, 515 mapToUserEntries(SearchList)); 516 517 Headers.SetSystemHeaderPrefixes(SystemHeaderPrefixes); 518 519 // If verbose, print the list of directories that will be searched. 520 if (Verbose) { 521 llvm::errs() << "#include \"...\" search starts here:\n"; 522 for (unsigned i = 0, e = SearchList.size(); i != e; ++i) { 523 if (i == NumQuoted) 524 llvm::errs() << "#include <...> search starts here:\n"; 525 StringRef Name = SearchList[i].Lookup.getName(); 526 const char *Suffix; 527 if (SearchList[i].Lookup.isNormalDir()) 528 Suffix = ""; 529 else if (SearchList[i].Lookup.isFramework()) 530 Suffix = " (framework directory)"; 531 else { 532 assert(SearchList[i].Lookup.isHeaderMap() && "Unknown DirectoryLookup"); 533 Suffix = " (headermap)"; 534 } 535 llvm::errs() << " " << Name << Suffix << "\n"; 536 } 537 llvm::errs() << "End of search list.\n"; 538 } 539 } 540 541 void clang::ApplyHeaderSearchOptions(HeaderSearch &HS, 542 const HeaderSearchOptions &HSOpts, 543 const LangOptions &Lang, 544 const llvm::Triple &Triple) { 545 InitHeaderSearch Init(HS, HSOpts.Verbose, HSOpts.Sysroot); 546 547 // Add the user defined entries. 548 for (unsigned i = 0, e = HSOpts.UserEntries.size(); i != e; ++i) { 549 const HeaderSearchOptions::Entry &E = HSOpts.UserEntries[i]; 550 if (E.IgnoreSysRoot) { 551 Init.AddUnmappedPath(E.Path, E.Group, E.IsFramework, i); 552 } else { 553 Init.AddPath(E.Path, E.Group, E.IsFramework, i); 554 } 555 } 556 557 Init.AddDefaultIncludePaths(Lang, Triple, HSOpts); 558 559 for (unsigned i = 0, e = HSOpts.SystemHeaderPrefixes.size(); i != e; ++i) 560 Init.AddSystemHeaderPrefix(HSOpts.SystemHeaderPrefixes[i].Prefix, 561 HSOpts.SystemHeaderPrefixes[i].IsSystemHeader); 562 563 if (HSOpts.UseBuiltinIncludes) { 564 // Set up the builtin include directory in the module map. 565 SmallString<128> P = StringRef(HSOpts.ResourceDir); 566 llvm::sys::path::append(P, "include"); 567 if (auto Dir = HS.getFileMgr().getOptionalDirectoryRef(P)) 568 HS.getModuleMap().setBuiltinIncludeDir(*Dir); 569 } 570 571 Init.Realize(Lang); 572 } 573