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(std::string(Prefix), 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);
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(MappedPathStorage);
125    if (CanPrefixSysroot(MappedPathStr)) {
126      return AddUnmappedPath(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(MappedPathStorage);
142
143  // If use system headers while cross-compiling, emit the warning.
144  if (HasSysroot && (MappedPathStr.starts_with("/usr/include") ||
145                     MappedPathStr.starts_with("/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(MappedPathStr)) {
162    IncludePath.emplace_back(Group, DirectoryLookup(*DE, Type, isFramework),
163                             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(MappedPathStr)) {
171      if (const HeaderMap *HM = Headers.CreateHeaderMap(*FE)) {
172        // It is a headermap, add it to the search path.
173        IncludePath.emplace_back(
174            Group, DirectoryLookup(HM, Type, Group == IndexHeaderMap),
175            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(Base + "/" + Arch + "/" + Version + "/include/c++",
191          CXXSystem, false);
192  AddPath(Base + "/" + Arch + "/" + Version + "/include/c++/" + Arch,
193          CXXSystem, false);
194  AddPath(Base + "/" + Arch + "/" + Version + "/include/c++/backward",
195          CXXSystem, 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("/usr/local/include", System, 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(P, "include");
225    AddUnmappedPath(P, ExternCSystem, 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(dirs, ":");
238    for (StringRef dir : dirs)
239      AddPath(dir, ExternCSystem, 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("/usr/include/w32api", System, false);
249      break;
250    case llvm::Triple::GNU:
251      break;
252    }
253    break;
254  default:
255    break;
256  }
257
258  AddPath("/usr/include", ExternCSystem, 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("/usr/lib/gcc", "i686-pc-cygwin", "4.7.3");
276      AddMinGWCPlusPlusIncludePaths("/usr/lib/gcc", "i686-pc-cygwin", "4.5.3");
277      AddMinGWCPlusPlusIncludePaths("/usr/lib/gcc", "i686-pc-cygwin", "4.3.4");
278      // g++-4 / Cygwin-1.5
279      AddMinGWCPlusPlusIncludePaths("/usr/lib/gcc", "i686-pc-cygwin", "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("/System/DriverKit/System/Library/Frameworks", System, true);
348      } else {
349        AddPath("/System/Library/Frameworks", System, true);
350        AddPath("/Library/Frameworks", System, true);
351      }
352    }
353    return;
354  }
355
356  if (Lang.CPlusPlus && !Lang.AsmPreprocessor &&
357      HSOpts.UseStandardCXXIncludes && HSOpts.UseStandardSystemIncludes) {
358    if (HSOpts.UseLibcxx) {
359      AddPath("/usr/include/c++/v1", CXXSystem, false);
360    } else {
361      AddDefaultCPlusPlusIncludePaths(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(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(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(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(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(Infos.size());
459  llvm::transform(Infos, std::back_inserter(Lookups),
460                  [](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({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(IncludePath.size());
480
481  // Quoted arguments go first.
482  for (auto &Include : IncludePath)
483    if (Include.Group == Quoted)
484      SearchList.push_back(Include);
485
486  // Deduplicate and remember index.
487  RemoveDuplicates(SearchList, 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(Include);
493
494  RemoveDuplicates(SearchList, 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(Include);
505
506  for (auto &Include : IncludePath)
507    if (Include.Group == After)
508      SearchList.push_back(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, NumQuoted, Verbose);
514  NumAngled -= NonSystemRemoved;
515
516  Headers.SetSearchPaths(extractLookups(SearchList), NumQuoted, NumAngled,
517                         mapToUserEntries(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(E.Path, E.Group, E.IsFramework, i);
554    } else {
555      Init.AddPath(E.Path, E.Group, E.IsFramework, i);
556    }
557  }
558
559  Init.AddDefaultIncludePaths(Lang, Triple, HSOpts);
560
561  for (unsigned i = 0, e = HSOpts.SystemHeaderPrefixes.size(); i != e; ++i)
562    Init.AddSystemHeaderPrefix(HSOpts.SystemHeaderPrefixes[i].Prefix,
563                               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(P, "include");
569    if (auto Dir = HS.getFileMgr().getOptionalDirectoryRef(P))
570      HS.getModuleMap().setBuiltinIncludeDir(*Dir);
571  }
572
573  Init.Realize(Lang);
574}
575