xref: /freebsd/contrib/llvm-project/clang/lib/Lex/InitHeaderSearch.cpp (revision e64bea71c21eb42e97aa615188ba91f6cce0d36d)
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 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 
96 static 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 
104 bool 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 
121 bool 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 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 
173 void 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 
211 bool 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::NaCl:
226   case llvm::Triple::NetBSD:
227   case llvm::Triple::OpenBSD:
228   case llvm::Triple::PS4:
229   case llvm::Triple::PS5:
230   case llvm::Triple::RTEMS:
231   case llvm::Triple::Solaris:
232   case llvm::Triple::UEFI:
233   case llvm::Triple::WASI:
234   case llvm::Triple::Win32:
235   case llvm::Triple::ZOS:
236     return false;
237 
238   case llvm::Triple::UnknownOS:
239     if (triple.isWasm() || triple.isAppleMachO())
240       return false;
241     break;
242 
243   default:
244     break;
245   }
246 
247   if (triple.isOSDarwin())
248     return false;
249 
250   return true; // Everything else uses AddDefaultIncludePaths().
251 }
252 
253 void InitHeaderSearch::AddDefaultIncludePaths(
254     const LangOptions &Lang, const llvm::Triple &triple,
255     const HeaderSearchOptions &HSOpts) {
256   // NB: This code path is going away. All of the logic is moving into the
257   // driver which has the information necessary to do target-specific
258   // selections of default include paths. Each target which moves there will be
259   // exempted from this logic in ShouldAddDefaultIncludePaths() until we can
260   // delete the entire pile of code.
261   if (!ShouldAddDefaultIncludePaths(triple))
262     return;
263 
264   if (Lang.CPlusPlus && !Lang.AsmPreprocessor &&
265       HSOpts.UseStandardCXXIncludes && HSOpts.UseStandardSystemIncludes) {
266     if (HSOpts.UseLibcxx) {
267       AddPath("/usr/include/c++/v1", CXXSystem, false);
268     }
269   }
270 
271   AddDefaultCIncludePaths(triple, HSOpts);
272 }
273 
274 /// If there are duplicate directory entries in the specified search list,
275 /// remove the later (dead) ones.  Returns the number of non-system headers
276 /// removed, which is used to update NumAngled.
277 static unsigned RemoveDuplicates(std::vector<DirectoryLookupInfo> &SearchList,
278                                  unsigned First, bool Verbose) {
279   llvm::SmallPtrSet<const DirectoryEntry *, 8> SeenDirs;
280   llvm::SmallPtrSet<const DirectoryEntry *, 8> SeenFrameworkDirs;
281   llvm::SmallPtrSet<const HeaderMap *, 8> SeenHeaderMaps;
282   unsigned NonSystemRemoved = 0;
283   for (unsigned i = First; i != SearchList.size(); ++i) {
284     unsigned DirToRemove = i;
285 
286     const DirectoryLookup &CurEntry = SearchList[i].Lookup;
287 
288     if (CurEntry.isNormalDir()) {
289       // If this isn't the first time we've seen this dir, remove it.
290       if (SeenDirs.insert(CurEntry.getDir()).second)
291         continue;
292     } else if (CurEntry.isFramework()) {
293       // If this isn't the first time we've seen this framework dir, remove it.
294       if (SeenFrameworkDirs.insert(CurEntry.getFrameworkDir()).second)
295         continue;
296     } else {
297       assert(CurEntry.isHeaderMap() && "Not a headermap or normal dir?");
298       // If this isn't the first time we've seen this headermap, remove it.
299       if (SeenHeaderMaps.insert(CurEntry.getHeaderMap()).second)
300         continue;
301     }
302 
303     // If we have a normal #include dir/framework/headermap that is shadowed
304     // later in the chain by a system include location, we actually want to
305     // ignore the user's request and drop the user dir... keeping the system
306     // dir.  This is weird, but required to emulate GCC's search path correctly.
307     //
308     // Since dupes of system dirs are rare, just rescan to find the original
309     // that we're nuking instead of using a DenseMap.
310     if (CurEntry.getDirCharacteristic() != SrcMgr::C_User) {
311       // Find the dir that this is the same of.
312       unsigned FirstDir;
313       for (FirstDir = First;; ++FirstDir) {
314         assert(FirstDir != i && "Didn't find dupe?");
315 
316         const DirectoryLookup &SearchEntry = SearchList[FirstDir].Lookup;
317 
318         // If these are different lookup types, then they can't be the dupe.
319         if (SearchEntry.getLookupType() != CurEntry.getLookupType())
320           continue;
321 
322         bool isSame;
323         if (CurEntry.isNormalDir())
324           isSame = SearchEntry.getDir() == CurEntry.getDir();
325         else if (CurEntry.isFramework())
326           isSame = SearchEntry.getFrameworkDir() == CurEntry.getFrameworkDir();
327         else {
328           assert(CurEntry.isHeaderMap() && "Not a headermap or normal dir?");
329           isSame = SearchEntry.getHeaderMap() == CurEntry.getHeaderMap();
330         }
331 
332         if (isSame)
333           break;
334       }
335 
336       // If the first dir in the search path is a non-system dir, zap it
337       // instead of the system one.
338       if (SearchList[FirstDir].Lookup.getDirCharacteristic() == SrcMgr::C_User)
339         DirToRemove = FirstDir;
340     }
341 
342     if (Verbose) {
343       llvm::errs() << "ignoring duplicate directory \""
344                    << CurEntry.getName() << "\"\n";
345       if (DirToRemove != i)
346         llvm::errs() << "  as it is a non-system directory that duplicates "
347                      << "a system directory\n";
348     }
349     if (DirToRemove != i)
350       ++NonSystemRemoved;
351 
352     // This is reached if the current entry is a duplicate.  Remove the
353     // DirToRemove (usually the current dir).
354     SearchList.erase(SearchList.begin()+DirToRemove);
355     --i;
356   }
357   return NonSystemRemoved;
358 }
359 
360 /// Extract DirectoryLookups from DirectoryLookupInfos.
361 static std::vector<DirectoryLookup>
362 extractLookups(const std::vector<DirectoryLookupInfo> &Infos) {
363   std::vector<DirectoryLookup> Lookups;
364   Lookups.reserve(Infos.size());
365   llvm::transform(Infos, std::back_inserter(Lookups),
366                   [](const DirectoryLookupInfo &Info) { return Info.Lookup; });
367   return Lookups;
368 }
369 
370 /// Collect the mapping between indices of DirectoryLookups and UserEntries.
371 static llvm::DenseMap<unsigned, unsigned>
372 mapToUserEntries(const std::vector<DirectoryLookupInfo> &Infos) {
373   llvm::DenseMap<unsigned, unsigned> LookupsToUserEntries;
374   for (unsigned I = 0, E = Infos.size(); I < E; ++I) {
375     // Check whether this DirectoryLookup maps to a HeaderSearch::UserEntry.
376     if (Infos[I].UserEntryIdx)
377       LookupsToUserEntries.insert({I, *Infos[I].UserEntryIdx});
378   }
379   return LookupsToUserEntries;
380 }
381 
382 void InitHeaderSearch::Realize(const LangOptions &Lang) {
383   // Concatenate ANGLE+SYSTEM+AFTER chains together into SearchList.
384   std::vector<DirectoryLookupInfo> SearchList;
385   SearchList.reserve(IncludePath.size());
386 
387   // Quoted arguments go first.
388   for (auto &Include : IncludePath)
389     if (Include.Group == Quoted)
390       SearchList.push_back(Include);
391 
392   // Deduplicate and remember index.
393   RemoveDuplicates(SearchList, 0, Verbose);
394   unsigned NumQuoted = SearchList.size();
395 
396   for (auto &Include : IncludePath)
397     if (Include.Group == Angled)
398       SearchList.push_back(Include);
399 
400   RemoveDuplicates(SearchList, NumQuoted, Verbose);
401   unsigned NumAngled = SearchList.size();
402 
403   for (auto &Include : IncludePath)
404     if (Include.Group == System || Include.Group == ExternCSystem ||
405         (!Lang.ObjC && !Lang.CPlusPlus && Include.Group == CSystem) ||
406         (/*FIXME !Lang.ObjC && */ Lang.CPlusPlus &&
407          Include.Group == CXXSystem) ||
408         (Lang.ObjC && !Lang.CPlusPlus && Include.Group == ObjCSystem) ||
409         (Lang.ObjC && Lang.CPlusPlus && Include.Group == ObjCXXSystem))
410       SearchList.push_back(Include);
411 
412   for (auto &Include : IncludePath)
413     if (Include.Group == After)
414       SearchList.push_back(Include);
415 
416   // Remove duplicates across both the Angled and System directories.  GCC does
417   // this and failing to remove duplicates across these two groups breaks
418   // #include_next.
419   unsigned NonSystemRemoved = RemoveDuplicates(SearchList, NumQuoted, Verbose);
420   NumAngled -= NonSystemRemoved;
421 
422   Headers.SetSearchPaths(extractLookups(SearchList), NumQuoted, NumAngled,
423                          mapToUserEntries(SearchList));
424 
425   Headers.SetSystemHeaderPrefixes(SystemHeaderPrefixes);
426 
427   // If verbose, print the list of directories that will be searched.
428   if (Verbose) {
429     llvm::errs() << "#include \"...\" search starts here:\n";
430     for (unsigned i = 0, e = SearchList.size(); i != e; ++i) {
431       if (i == NumQuoted)
432         llvm::errs() << "#include <...> search starts here:\n";
433       StringRef Name = SearchList[i].Lookup.getName();
434       const char *Suffix;
435       if (SearchList[i].Lookup.isNormalDir())
436         Suffix = "";
437       else if (SearchList[i].Lookup.isFramework())
438         Suffix = " (framework directory)";
439       else {
440         assert(SearchList[i].Lookup.isHeaderMap() && "Unknown DirectoryLookup");
441         Suffix = " (headermap)";
442       }
443       llvm::errs() << " " << Name << Suffix << "\n";
444     }
445     llvm::errs() << "End of search list.\n";
446   }
447 }
448 
449 void clang::ApplyHeaderSearchOptions(HeaderSearch &HS,
450                                      const HeaderSearchOptions &HSOpts,
451                                      const LangOptions &Lang,
452                                      const llvm::Triple &Triple) {
453   InitHeaderSearch Init(HS, HSOpts.Verbose, HSOpts.Sysroot);
454 
455   // Add the user defined entries.
456   for (unsigned i = 0, e = HSOpts.UserEntries.size(); i != e; ++i) {
457     const HeaderSearchOptions::Entry &E = HSOpts.UserEntries[i];
458     if (E.IgnoreSysRoot) {
459       Init.AddUnmappedPath(E.Path, E.Group, E.IsFramework, i);
460     } else {
461       Init.AddPath(E.Path, E.Group, E.IsFramework, i);
462     }
463   }
464 
465   Init.AddDefaultIncludePaths(Lang, Triple, HSOpts);
466 
467   for (unsigned i = 0, e = HSOpts.SystemHeaderPrefixes.size(); i != e; ++i)
468     Init.AddSystemHeaderPrefix(HSOpts.SystemHeaderPrefixes[i].Prefix,
469                                HSOpts.SystemHeaderPrefixes[i].IsSystemHeader);
470 
471   if (HSOpts.UseBuiltinIncludes) {
472     // Set up the builtin include directory in the module map.
473     SmallString<128> P = StringRef(HSOpts.ResourceDir);
474     llvm::sys::path::append(P, "include");
475     if (auto Dir = HS.getFileMgr().getOptionalDirectoryRef(P))
476       HS.getModuleMap().setBuiltinIncludeDir(*Dir);
477   }
478 
479   Init.Realize(Lang);
480 }
481