brintos

brintos / llvm-project-archived public Read only

0
0
Text · 9.4 KiB · f8f708f Raw
302 lines · cpp
1//===- DirectoryScanner.cpp -----------------------------------------------===//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#include "clang/InstallAPI/DirectoryScanner.h"10#include "llvm/ADT/StringRef.h"11#include "llvm/ADT/StringSwitch.h"12 13using namespace llvm;14using namespace llvm::MachO;15 16namespace clang::installapi {17 18HeaderSeq DirectoryScanner::getHeaders(ArrayRef<Library> Libraries) {19  HeaderSeq Headers;20  for (const Library &Lib : Libraries)21    llvm::append_range(Headers, Lib.Headers);22  return Headers;23}24 25llvm::Error DirectoryScanner::scan(StringRef Directory) {26  if (Mode == ScanMode::ScanFrameworks)27    return scanForFrameworks(Directory);28 29  return scanForUnwrappedLibraries(Directory);30}31 32llvm::Error DirectoryScanner::scanForUnwrappedLibraries(StringRef Directory) {33  // Check some known sub-directory locations.34  auto GetDirectory = [&](const char *Sub) -> OptionalDirectoryEntryRef {35    SmallString<PATH_MAX> Path(Directory);36    sys::path::append(Path, Sub);37    return FM.getOptionalDirectoryRef(Path);38  };39 40  auto DirPublic = GetDirectory("usr/include");41  auto DirPrivate = GetDirectory("usr/local/include");42  if (!DirPublic && !DirPrivate) {43    std::error_code ec = std::make_error_code(std::errc::not_a_directory);44    return createStringError(ec,45                             "cannot find any public (usr/include) or private "46                             "(usr/local/include) header directory");47  }48 49  Library &Lib = getOrCreateLibrary(Directory, Libraries);50  Lib.IsUnwrappedDylib = true;51 52  if (DirPublic)53    if (Error Err = scanHeaders(DirPublic->getName(), Lib, HeaderType::Public,54                                Directory))55      return Err;56 57  if (DirPrivate)58    if (Error Err = scanHeaders(DirPrivate->getName(), Lib, HeaderType::Private,59                                Directory))60      return Err;61 62  return Error::success();63}64 65static bool isFramework(StringRef Path) {66  while (Path.back() == '/')67    Path = Path.slice(0, Path.size() - 1);68 69  return llvm::StringSwitch<bool>(llvm::sys::path::extension(Path))70      .Case(".framework", true)71      .Default(false);72}73 74Library &75DirectoryScanner::getOrCreateLibrary(StringRef Path,76                                     std::vector<Library> &Libs) const {77  if (Path.consume_front(RootPath) && Path.empty())78    Path = "/";79 80  auto LibIt =81      find_if(Libs, [Path](const Library &L) { return L.getPath() == Path; });82  if (LibIt != Libs.end())83    return *LibIt;84 85  Libs.emplace_back(Path);86  return Libs.back();87}88 89Error DirectoryScanner::scanHeaders(StringRef Path, Library &Lib,90                                    HeaderType Type, StringRef BasePath,91                                    StringRef ParentPath) const {92  std::error_code ec;93  auto &FS = FM.getVirtualFileSystem();94  PathSeq SubDirectories;95  for (vfs::directory_iterator i = FS.dir_begin(Path, ec), ie; i != ie;96       i.increment(ec)) {97    StringRef HeaderPath = i->path();98    if (ec)99      return createStringError(ec, "unable to read: " + HeaderPath);100 101    if (sys::fs::is_symlink_file(HeaderPath))102      continue;103 104    // Ignore tmp files from unifdef.105    const StringRef Filename = sys::path::filename(HeaderPath);106    if (Filename.starts_with("."))107      continue;108 109    // If it is a directory, remember the subdirectory.110    if (FM.getOptionalDirectoryRef(HeaderPath))111      SubDirectories.push_back(HeaderPath.str());112 113    if (!isHeaderFile(HeaderPath))114      continue;115 116    // Skip files that do not exist. This usually happens for broken symlinks.117    if (FS.status(HeaderPath) == std::errc::no_such_file_or_directory)118      continue;119 120    auto IncludeName = createIncludeHeaderName(HeaderPath);121    Lib.addHeaderFile(HeaderPath, Type,122                      IncludeName.has_value() ? IncludeName.value() : "");123  }124 125  // Go through the subdirectories.126  // Sort the sub-directory first since different file systems might have127  // different traverse order.128  llvm::sort(SubDirectories);129  if (ParentPath.empty())130    ParentPath = Path;131  for (const StringRef Dir : SubDirectories)132    if (Error Err = scanHeaders(Dir, Lib, Type, BasePath, ParentPath))133      return Err;134 135  return Error::success();136}137 138llvm::Error139DirectoryScanner::scanMultipleFrameworks(StringRef Directory,140                                         std::vector<Library> &Libs) const {141  std::error_code ec;142  auto &FS = FM.getVirtualFileSystem();143  for (vfs::directory_iterator i = FS.dir_begin(Directory, ec), ie; i != ie;144       i.increment(ec)) {145    StringRef Curr = i->path();146 147    // Skip files that do not exist. This usually happens for broken symlinks.148    if (ec == std::errc::no_such_file_or_directory) {149      ec.clear();150      continue;151    }152    if (ec)153      return createStringError(ec, Curr);154 155    if (sys::fs::is_symlink_file(Curr))156      continue;157 158    if (isFramework(Curr)) {159      if (!FM.getOptionalDirectoryRef(Curr))160        continue;161      Library &Framework = getOrCreateLibrary(Curr, Libs);162      if (Error Err = scanFrameworkDirectory(Curr, Framework))163        return Err;164    }165  }166 167  return Error::success();168}169 170llvm::Error171DirectoryScanner::scanSubFrameworksDirectory(StringRef Directory,172                                             std::vector<Library> &Libs) const {173  if (FM.getOptionalDirectoryRef(Directory))174    return scanMultipleFrameworks(Directory, Libs);175 176  std::error_code ec = std::make_error_code(std::errc::not_a_directory);177  return createStringError(ec, Directory);178}179 180/// FIXME: How to handle versions? For now scan them separately as independent181/// frameworks.182llvm::Error183DirectoryScanner::scanFrameworkVersionsDirectory(StringRef Path,184                                                 Library &Lib) const {185  std::error_code ec;186  auto &FS = FM.getVirtualFileSystem();187  for (vfs::directory_iterator i = FS.dir_begin(Path, ec), ie; i != ie;188       i.increment(ec)) {189    const StringRef Curr = i->path();190 191    // Skip files that do not exist. This usually happens for broken symlinks.192    if (ec == std::errc::no_such_file_or_directory) {193      ec.clear();194      continue;195    }196    if (ec)197      return createStringError(ec, Curr);198 199    if (sys::fs::is_symlink_file(Curr))200      continue;201 202    // Each version should be a framework directory.203    if (!FM.getOptionalDirectoryRef(Curr))204      continue;205 206    Library &VersionedFramework =207        getOrCreateLibrary(Curr, Lib.FrameworkVersions);208    if (Error Err = scanFrameworkDirectory(Curr, VersionedFramework))209      return Err;210  }211 212  return Error::success();213}214 215llvm::Error DirectoryScanner::scanFrameworkDirectory(StringRef Path,216                                                     Library &Framework) const {217  // If the framework is inside Kernel or IOKit, scan headers in the different218  // directories separately.219  Framework.IsUnwrappedDylib =220      Path.contains("Kernel.framework") || Path.contains("IOKit.framework");221 222  // Unfortunately we cannot identify symlinks in the VFS. We assume that if223  // there is a Versions directory, then we have symlinks and directly proceed224  // to the Versions folder.225  std::error_code ec;226  auto &FS = FM.getVirtualFileSystem();227 228  for (vfs::directory_iterator i = FS.dir_begin(Path, ec), ie; i != ie;229       i.increment(ec)) {230    StringRef Curr = i->path();231    // Skip files that do not exist. This usually happens for broken symlinks.232    if (ec == std::errc::no_such_file_or_directory) {233      ec.clear();234      continue;235    }236 237    if (ec)238      return createStringError(ec, Curr);239 240    if (sys::fs::is_symlink_file(Curr))241      continue;242 243    StringRef FileName = sys::path::filename(Curr);244    // Scan all "public" headers.245    if (FileName.contains("Headers")) {246      if (Error Err = scanHeaders(Curr, Framework, HeaderType::Public, Curr))247        return Err;248      continue;249    }250    // Scan all "private" headers.251    if (FileName.contains("PrivateHeaders")) {252      if (Error Err = scanHeaders(Curr, Framework, HeaderType::Private, Curr))253        return Err;254      continue;255    }256    // Scan sub frameworks.257    if (FileName.contains("Frameworks")) {258      if (Error Err = scanSubFrameworksDirectory(Curr, Framework.SubFrameworks))259        return Err;260      continue;261    }262    // Check for versioned frameworks.263    if (FileName.contains("Versions")) {264      if (Error Err = scanFrameworkVersionsDirectory(Curr, Framework))265        return Err;266      continue;267    }268  }269 270  return Error::success();271}272 273llvm::Error DirectoryScanner::scanForFrameworks(StringRef Directory) {274  RootPath = "";275 276  // Expect a certain directory structure and naming convention to find277  // frameworks.278  static const char *SubDirectories[] = {"System/Library/Frameworks/",279                                         "System/Library/PrivateFrameworks/",280                                         "System/Library/SubFrameworks"};281 282  // Check if the directory is already a framework.283  if (isFramework(Directory)) {284    Library &Framework = getOrCreateLibrary(Directory, Libraries);285    if (Error Err = scanFrameworkDirectory(Directory, Framework))286      return Err;287    return Error::success();288  }289 290  // Check known sub-directory locations.291  for (const auto *SubDir : SubDirectories) {292    SmallString<PATH_MAX> Path(Directory);293    sys::path::append(Path, SubDir);294 295    if (Error Err = scanMultipleFrameworks(Path, Libraries))296      return Err;297  }298 299  return Error::success();300}301} // namespace clang::installapi302