brintos

brintos / llvm-project-archived public Read only

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