clang 23.0.0git
InitHeaderSearch.cpp
Go to the documentation of this file.
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
15#include "clang/Config/config.h" // C_INCLUDE_DIRS
16#include "clang/Lex/HeaderMap.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 be
45/// 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 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 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 this
82 /// 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#else
100 return llvm::sys::path::is_absolute(Path);
101#endif
102}
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 header
108 // 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.
139 if (Group == Quoted || Group == Angled) {
141 } else if (Group == ExternCSystem) {
143 } else {
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 to
155 // 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 positioned
184 // just prior C include dirs.
185 if (HSOpts.UseBuiltinIncludes) {
186 // Ignore the sys root, we *always* look for clang headers relative to
187 // 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 if
194 // 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::WASIp1:
234 case llvm::Triple::WASIp2:
235 case llvm::Triple::WASIp3:
236 case llvm::Triple::Win32:
237 case llvm::Triple::ZOS:
238 return false;
239
240 case llvm::Triple::UnknownOS:
241 if (triple.isWasm() || triple.isAppleMachO())
242 return false;
243 break;
244
245 default:
246 break;
247 }
248
249 if (triple.isOSDarwin())
250 return false;
251
252 return true; // Everything else uses AddDefaultIncludePaths().
253}
254
255void InitHeaderSearch::AddDefaultIncludePaths(
256 const LangOptions &Lang, const llvm::Triple &triple,
257 const HeaderSearchOptions &HSOpts) {
258 // NB: This code path is going away. All of the logic is moving into the
259 // driver which has the information necessary to do target-specific
260 // selections of default include paths. Each target which moves there will be
261 // exempted from this logic in ShouldAddDefaultIncludePaths() until we can
262 // delete the entire pile of code.
263 if (!ShouldAddDefaultIncludePaths(triple))
264 return;
265
266 if (Lang.CPlusPlus && !Lang.AsmPreprocessor &&
268 if (HSOpts.UseLibcxx) {
269 AddPath("/usr/include/c++/v1", CXXSystem, false);
270 }
271 }
272
273 AddDefaultCIncludePaths(triple, HSOpts);
274}
275
276/// If there are duplicate directory entries in the specified search list,
277/// remove the later (dead) ones. Returns the number of non-system headers
278/// removed, which is used to update NumAngled.
279static unsigned RemoveDuplicates(std::vector<DirectoryLookupInfo> &SearchList,
280 unsigned First, bool Verbose) {
284 unsigned NonSystemRemoved = 0;
285 for (unsigned i = First; i != SearchList.size(); ++i) {
286 unsigned DirToRemove = i;
287
288 const DirectoryLookup &CurEntry = SearchList[i].Lookup;
289
290 if (CurEntry.isNormalDir()) {
291 // If this isn't the first time we've seen this dir, remove it.
292 if (SeenDirs.insert(CurEntry.getDir()).second)
293 continue;
294 } else if (CurEntry.isFramework()) {
295 // If this isn't the first time we've seen this framework dir, remove it.
296 if (SeenFrameworkDirs.insert(CurEntry.getFrameworkDir()).second)
297 continue;
298 } else {
299 assert(CurEntry.isHeaderMap() && "Not a headermap or normal dir?");
300 // If this isn't the first time we've seen this headermap, remove it.
301 if (SeenHeaderMaps.insert(CurEntry.getHeaderMap()).second)
302 continue;
303 }
304
305 // If we have a normal #include dir/framework/headermap that is shadowed
306 // later in the chain by a system include location, we actually want to
307 // ignore the user's request and drop the user dir... keeping the system
308 // dir. This is weird, but required to emulate GCC's search path correctly.
309 //
310 // Since dupes of system dirs are rare, just rescan to find the original
311 // that we're nuking instead of using a DenseMap.
312 if (CurEntry.getDirCharacteristic() != SrcMgr::C_User) {
313 // Find the dir that this is the same of.
314 unsigned FirstDir;
315 for (FirstDir = First;; ++FirstDir) {
316 assert(FirstDir != i && "Didn't find dupe?");
317
318 const DirectoryLookup &SearchEntry = SearchList[FirstDir].Lookup;
319
320 // If these are different lookup types, then they can't be the dupe.
321 if (SearchEntry.getLookupType() != CurEntry.getLookupType())
322 continue;
323
324 bool isSame;
325 if (CurEntry.isNormalDir())
326 isSame = SearchEntry.getDir() == CurEntry.getDir();
327 else if (CurEntry.isFramework())
328 isSame = SearchEntry.getFrameworkDir() == CurEntry.getFrameworkDir();
329 else {
330 assert(CurEntry.isHeaderMap() && "Not a headermap or normal dir?");
331 isSame = SearchEntry.getHeaderMap() == CurEntry.getHeaderMap();
332 }
333
334 if (isSame)
335 break;
336 }
337
338 // If the first dir in the search path is a non-system dir, zap it
339 // instead of the system one.
340 if (SearchList[FirstDir].Lookup.getDirCharacteristic() == SrcMgr::C_User)
341 DirToRemove = FirstDir;
342 }
343
344 if (Verbose) {
345 llvm::errs() << "ignoring duplicate directory \""
346 << CurEntry.getName() << "\"\n";
347 if (DirToRemove != i)
348 llvm::errs() << " as it is a non-system directory that duplicates "
349 << "a system directory\n";
350 }
351 if (DirToRemove != i)
352 ++NonSystemRemoved;
353
354 // This is reached if the current entry is a duplicate. Remove the
355 // DirToRemove (usually the current dir).
356 SearchList.erase(SearchList.begin()+DirToRemove);
357 --i;
358 }
359 return NonSystemRemoved;
360}
361
362/// Extract DirectoryLookups from DirectoryLookupInfos.
363static std::vector<DirectoryLookup>
364extractLookups(const std::vector<DirectoryLookupInfo> &Infos) {
365 std::vector<DirectoryLookup> Lookups;
366 Lookups.reserve(Infos.size());
367 llvm::transform(Infos, std::back_inserter(Lookups),
368 [](const DirectoryLookupInfo &Info) { return Info.Lookup; });
369 return Lookups;
370}
371
372/// Collect the mapping between indices of DirectoryLookups and UserEntries.
373static llvm::DenseMap<unsigned, unsigned>
374mapToUserEntries(const std::vector<DirectoryLookupInfo> &Infos) {
375 llvm::DenseMap<unsigned, unsigned> LookupsToUserEntries;
376 for (unsigned I = 0, E = Infos.size(); I < E; ++I) {
377 // Check whether this DirectoryLookup maps to a HeaderSearch::UserEntry.
378 if (Infos[I].UserEntryIdx)
379 LookupsToUserEntries.insert({I, *Infos[I].UserEntryIdx});
380 }
381 return LookupsToUserEntries;
382}
383
384void InitHeaderSearch::Realize(const LangOptions &Lang) {
385 // Concatenate ANGLE+SYSTEM+AFTER chains together into SearchList.
386 std::vector<DirectoryLookupInfo> SearchList;
387 SearchList.reserve(IncludePath.size());
388
389 // Quoted arguments go first.
390 for (auto &Include : IncludePath)
391 if (Include.Group == Quoted)
392 SearchList.push_back(Include);
393
394 // Deduplicate and remember index.
395 RemoveDuplicates(SearchList, 0, Verbose);
396 unsigned NumQuoted = SearchList.size();
397
398 for (auto &Include : IncludePath)
399 if (Include.Group == Angled)
400 SearchList.push_back(Include);
401
402 RemoveDuplicates(SearchList, NumQuoted, Verbose);
403 unsigned NumAngled = SearchList.size();
404
405 for (auto &Include : IncludePath)
406 if (Include.Group == System || Include.Group == ExternCSystem ||
407 (!Lang.ObjC && !Lang.CPlusPlus && Include.Group == CSystem) ||
408 (/*FIXME !Lang.ObjC && */ Lang.CPlusPlus &&
409 Include.Group == CXXSystem) ||
410 (Lang.ObjC && !Lang.CPlusPlus && Include.Group == ObjCSystem) ||
411 (Lang.ObjC && Lang.CPlusPlus && Include.Group == ObjCXXSystem))
412 SearchList.push_back(Include);
413
414 for (auto &Include : IncludePath)
415 if (Include.Group == After)
416 SearchList.push_back(Include);
417
418 // Remove duplicates across both the Angled and System directories. GCC does
419 // this and failing to remove duplicates across these two groups breaks
420 // #include_next.
421 unsigned NonSystemRemoved = RemoveDuplicates(SearchList, NumQuoted, Verbose);
422 NumAngled -= NonSystemRemoved;
423
424 Headers.SetSearchPaths(extractLookups(SearchList), NumQuoted, NumAngled,
425 mapToUserEntries(SearchList));
426
427 Headers.SetSystemHeaderPrefixes(SystemHeaderPrefixes);
428
429 // If verbose, print the list of directories that will be searched.
430 if (Verbose) {
431 llvm::errs() << "#include \"...\" search starts here:\n";
432 for (unsigned i = 0, e = SearchList.size(); i != e; ++i) {
433 if (i == NumQuoted)
434 llvm::errs() << "#include <...> search starts here:\n";
435 StringRef Name = SearchList[i].Lookup.getName();
436 const char *Suffix;
437 if (SearchList[i].Lookup.isNormalDir())
438 Suffix = "";
439 else if (SearchList[i].Lookup.isFramework())
440 Suffix = " (framework directory)";
441 else {
442 assert(SearchList[i].Lookup.isHeaderMap() && "Unknown DirectoryLookup");
443 Suffix = " (headermap)";
444 }
445 llvm::errs() << " " << Name << Suffix << "\n";
446 }
447 llvm::errs() << "End of search list.\n";
448 }
449}
450
452 const HeaderSearchOptions &HSOpts,
453 const LangOptions &Lang,
454 const llvm::Triple &Triple) {
455 InitHeaderSearch Init(HS, HSOpts.Verbose, HSOpts.Sysroot);
456
457 // Add the user defined entries.
458 for (unsigned i = 0, e = HSOpts.UserEntries.size(); i != e; ++i) {
459 const HeaderSearchOptions::Entry &E = HSOpts.UserEntries[i];
460 if (E.IgnoreSysRoot) {
461 Init.AddUnmappedPath(E.Path, E.Group, E.IsFramework, i);
462 } else {
463 Init.AddPath(E.Path, E.Group, E.IsFramework, i);
464 }
465 }
466
467 Init.AddDefaultIncludePaths(Lang, Triple, HSOpts);
468
469 for (unsigned i = 0, e = HSOpts.SystemHeaderPrefixes.size(); i != e; ++i)
470 Init.AddSystemHeaderPrefix(HSOpts.SystemHeaderPrefixes[i].Prefix,
471 HSOpts.SystemHeaderPrefixes[i].IsSystemHeader);
472
473 if (HSOpts.UseBuiltinIncludes) {
474 // Set up the builtin include directory in the module map.
475 SmallString<128> P = StringRef(HSOpts.ResourceDir);
476 llvm::sys::path::append(P, "include");
477 if (auto Dir = HS.getFileMgr().getOptionalDirectoryRef(P))
479 }
480
481 Init.Realize(Lang);
482}
Defines the clang::FileManager interface and associated types.
static unsigned RemoveDuplicates(std::vector< DirectoryLookupInfo > &SearchList, unsigned First, bool Verbose)
If there are duplicate directory entries in the specified search list, remove the later (dead) ones.
static llvm::DenseMap< unsigned, unsigned > mapToUserEntries(const std::vector< DirectoryLookupInfo > &Infos)
Collect the mapping between indices of DirectoryLookups and UserEntries.
static bool CanPrefixSysroot(StringRef Path)
static std::vector< DirectoryLookup > extractLookups(const std::vector< DirectoryLookupInfo > &Infos)
Extract DirectoryLookups from DirectoryLookupInfos.
Defines the clang::LangOptions interface.
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
DirectoryLookup - This class represents one entry in the search list that specifies the search order ...
SrcMgr::CharacteristicKind getDirCharacteristic() const
DirCharacteristic - The type of directory this is, one of the DirType enum values.
const DirectoryEntry * getFrameworkDir() const
getFrameworkDir - Return the directory that this framework refers to.
bool isFramework() const
isFramework - True if this is a framework directory.
bool isHeaderMap() const
isHeaderMap - Return true if this is a header map, not a normal directory.
StringRef getName() const
getName - Return the directory or filename corresponding to this lookup object.
LookupType_t getLookupType() const
getLookupType - Return the kind of directory lookup that this is: either a normal directory,...
const DirectoryEntry * getDir() const
getDir - Return the directory that this entry refers to.
bool isNormalDir() const
isNormalDir - Return true if this is a normal directory, not a header map.
const HeaderMap * getHeaderMap() const
getHeaderMap - Return the directory that this entry refers to.
OptionalFileEntryRef getOptionalFileRef(StringRef Filename, bool OpenFile=false, bool CacheFailure=true)
Get a FileEntryRef if it exists, without doing anything on error.
OptionalDirectoryEntryRef getOptionalDirectoryRef(StringRef DirName, bool CacheFailure=true)
Get a DirectoryEntryRef if it exists, without doing anything on error.
HeaderSearchOptions - Helper class for storing options related to the initialization of the HeaderSea...
unsigned Verbose
Whether header search information should be output as for -v.
std::vector< SystemHeaderPrefix > SystemHeaderPrefixes
User-specified system header prefixes.
std::string Sysroot
If non-empty, the directory to use as a "virtual system root" for include paths.
unsigned UseLibcxx
Use libc++ instead of the default libstdc++.
unsigned UseBuiltinIncludes
Include the compiler builtin includes.
unsigned UseStandardCXXIncludes
Include the system standard C++ library include search directories.
std::vector< Entry > UserEntries
User specified include entries.
std::string ResourceDir
The directory which holds the compiler resource files (builtin includes, etc.).
unsigned UseStandardSystemIncludes
Include the system standard include search directories.
Encapsulates the information needed to find the file referenced by a #include or #include_next,...
FileManager & getFileMgr() const
DiagnosticsEngine & getDiags() const
void SetSystemHeaderPrefixes(ArrayRef< std::pair< std::string, bool > > P)
Set the list of system header prefixes.
void SetSearchPaths(std::vector< DirectoryLookup > dirs, unsigned angledDirIdx, unsigned systemDirIdx, llvm::DenseMap< unsigned, unsigned > searchDirToHSEntry)
Interface for setting the file search paths.
const HeaderMap * CreateHeaderMap(FileEntryRef FE)
This method returns a HeaderMap for the specified FileEntry, uniquing them through the 'HeaderMaps' d...
ModuleMap & getModuleMap()
Retrieve the module map.
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
void setBuiltinIncludeDir(DirectoryEntryRef Dir)
Set the directory that contains Clang-supplied include files, such as our stdarg.h or tgmath....
Definition ModuleMap.h:405
CharacteristicKind
Indicates whether a file or directory holds normal user code, system code, or system code which is im...
@ HeaderSearch
Remove unused header search paths including header maps.
IncludeDirGroup
IncludeDirGroup - Identifies the group an include Entry belongs to, representing its relative positiv...
@ CXXSystem
Like System, but only used for C++.
@ Angled
Paths for '#include <>' added by '-I'.
@ CSystem
Like System, but only used for C.
@ System
Like Angled, but marks system directories.
@ Quoted
'#include ""' paths, added by 'gcc -iquote'.
@ ExternCSystem
Like System, but headers are implicitly wrapped in extern "C".
@ ObjCSystem
Like System, but only used for ObjC.
@ ObjCXXSystem
Like System, but only used for ObjC++.
@ After
Like System, but searched after the system directories.
The JSON file list parser is used to communicate input to InstallAPI.
void ApplyHeaderSearchOptions(HeaderSearch &HS, const HeaderSearchOptions &HSOpts, const LangOptions &Lang, const llvm::Triple &triple)
Apply the header search options to get given HeaderSearch object.
@ Type
The name was classified as a type.
Definition Sema.h:564
unsigned IgnoreSysRoot
IgnoreSysRoot - This is false if an absolute path should be treated relative to the sysroot,...