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

source code of clang/lib/Lex/InitHeaderSearch.cpp