Path: blob/main/contrib/llvm-project/clang/lib/InstallAPI/DirectoryScanner.cpp
35232 views
//===- DirectoryScanner.cpp -----------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "clang/InstallAPI/DirectoryScanner.h"9#include "llvm/ADT/StringRef.h"10#include "llvm/ADT/StringSwitch.h"11#include "llvm/TextAPI/DylibReader.h"1213using namespace llvm;14using namespace llvm::MachO;1516namespace clang::installapi {1718HeaderSeq DirectoryScanner::getHeaders(ArrayRef<Library> Libraries) {19HeaderSeq Headers;20for (const Library &Lib : Libraries)21llvm::append_range(Headers, Lib.Headers);22return Headers;23}2425llvm::Error DirectoryScanner::scan(StringRef Directory) {26if (Mode == ScanMode::ScanFrameworks)27return scanForFrameworks(Directory);2829return scanForUnwrappedLibraries(Directory);30}3132llvm::Error DirectoryScanner::scanForUnwrappedLibraries(StringRef Directory) {33// Check some known sub-directory locations.34auto GetDirectory = [&](const char *Sub) -> OptionalDirectoryEntryRef {35SmallString<PATH_MAX> Path(Directory);36sys::path::append(Path, Sub);37return FM.getOptionalDirectoryRef(Path);38};3940auto DirPublic = GetDirectory("usr/include");41auto DirPrivate = GetDirectory("usr/local/include");42if (!DirPublic && !DirPrivate) {43std::error_code ec = std::make_error_code(std::errc::not_a_directory);44return createStringError(ec,45"cannot find any public (usr/include) or private "46"(usr/local/include) header directory");47}4849Library &Lib = getOrCreateLibrary(Directory, Libraries);50Lib.IsUnwrappedDylib = true;5152if (DirPublic)53if (Error Err = scanHeaders(DirPublic->getName(), Lib, HeaderType::Public,54Directory))55return Err;5657if (DirPrivate)58if (Error Err = scanHeaders(DirPrivate->getName(), Lib, HeaderType::Private,59Directory))60return Err;6162return Error::success();63}6465static bool isFramework(StringRef Path) {66while (Path.back() == '/')67Path = Path.slice(0, Path.size() - 1);6869return llvm::StringSwitch<bool>(llvm::sys::path::extension(Path))70.Case(".framework", true)71.Default(false);72}7374Library &75DirectoryScanner::getOrCreateLibrary(StringRef Path,76std::vector<Library> &Libs) const {77if (Path.consume_front(RootPath) && Path.empty())78Path = "/";7980auto LibIt =81find_if(Libs, [Path](const Library &L) { return L.getPath() == Path; });82if (LibIt != Libs.end())83return *LibIt;8485Libs.emplace_back(Path);86return Libs.back();87}8889Error DirectoryScanner::scanHeaders(StringRef Path, Library &Lib,90HeaderType Type, StringRef BasePath,91StringRef ParentPath) const {92std::error_code ec;93auto &FS = FM.getVirtualFileSystem();94PathSeq SubDirectories;95for (vfs::directory_iterator i = FS.dir_begin(Path, ec), ie; i != ie;96i.increment(ec)) {97StringRef HeaderPath = i->path();98if (ec)99return createStringError(ec, "unable to read: " + HeaderPath);100101if (sys::fs::is_symlink_file(HeaderPath))102continue;103104// Ignore tmp files from unifdef.105const StringRef Filename = sys::path::filename(HeaderPath);106if (Filename.starts_with("."))107continue;108109// If it is a directory, remember the subdirectory.110if (FM.getOptionalDirectoryRef(HeaderPath))111SubDirectories.push_back(HeaderPath.str());112113if (!isHeaderFile(HeaderPath))114continue;115116// Skip files that do not exist. This usually happens for broken symlinks.117if (FS.status(HeaderPath) == std::errc::no_such_file_or_directory)118continue;119120auto IncludeName = createIncludeHeaderName(HeaderPath);121Lib.addHeaderFile(HeaderPath, Type,122IncludeName.has_value() ? IncludeName.value() : "");123}124125// Go through the subdirectories.126// Sort the sub-directory first since different file systems might have127// different traverse order.128llvm::sort(SubDirectories);129if (ParentPath.empty())130ParentPath = Path;131for (const StringRef Dir : SubDirectories)132return scanHeaders(Dir, Lib, Type, BasePath, ParentPath);133134return Error::success();135}136137llvm::Error138DirectoryScanner::scanMultipleFrameworks(StringRef Directory,139std::vector<Library> &Libs) const {140std::error_code ec;141auto &FS = FM.getVirtualFileSystem();142for (vfs::directory_iterator i = FS.dir_begin(Directory, ec), ie; i != ie;143i.increment(ec)) {144StringRef Curr = i->path();145146// Skip files that do not exist. This usually happens for broken symlinks.147if (ec == std::errc::no_such_file_or_directory) {148ec.clear();149continue;150}151if (ec)152return createStringError(ec, Curr);153154if (sys::fs::is_symlink_file(Curr))155continue;156157if (isFramework(Curr)) {158if (!FM.getOptionalDirectoryRef(Curr))159continue;160Library &Framework = getOrCreateLibrary(Curr, Libs);161if (Error Err = scanFrameworkDirectory(Curr, Framework))162return Err;163}164}165166return Error::success();167}168169llvm::Error170DirectoryScanner::scanSubFrameworksDirectory(StringRef Directory,171std::vector<Library> &Libs) const {172if (FM.getOptionalDirectoryRef(Directory))173return scanMultipleFrameworks(Directory, Libs);174175std::error_code ec = std::make_error_code(std::errc::not_a_directory);176return createStringError(ec, Directory);177}178179/// FIXME: How to handle versions? For now scan them separately as independent180/// frameworks.181llvm::Error182DirectoryScanner::scanFrameworkVersionsDirectory(StringRef Path,183Library &Lib) const {184std::error_code ec;185auto &FS = FM.getVirtualFileSystem();186for (vfs::directory_iterator i = FS.dir_begin(Path, ec), ie; i != ie;187i.increment(ec)) {188const StringRef Curr = i->path();189190// Skip files that do not exist. This usually happens for broken symlinks.191if (ec == std::errc::no_such_file_or_directory) {192ec.clear();193continue;194}195if (ec)196return createStringError(ec, Curr);197198if (sys::fs::is_symlink_file(Curr))199continue;200201// Each version should be a framework directory.202if (!FM.getOptionalDirectoryRef(Curr))203continue;204205Library &VersionedFramework =206getOrCreateLibrary(Curr, Lib.FrameworkVersions);207if (Error Err = scanFrameworkDirectory(Curr, VersionedFramework))208return Err;209}210211return Error::success();212}213214llvm::Error DirectoryScanner::scanFrameworkDirectory(StringRef Path,215Library &Framework) const {216// If the framework is inside Kernel or IOKit, scan headers in the different217// directories separately.218Framework.IsUnwrappedDylib =219Path.contains("Kernel.framework") || Path.contains("IOKit.framework");220221// Unfortunately we cannot identify symlinks in the VFS. We assume that if222// there is a Versions directory, then we have symlinks and directly proceed223// to the Versions folder.224std::error_code ec;225auto &FS = FM.getVirtualFileSystem();226227for (vfs::directory_iterator i = FS.dir_begin(Path, ec), ie; i != ie;228i.increment(ec)) {229StringRef Curr = i->path();230// Skip files that do not exist. This usually happens for broken symlinks.231if (ec == std::errc::no_such_file_or_directory) {232ec.clear();233continue;234}235236if (ec)237return createStringError(ec, Curr);238239if (sys::fs::is_symlink_file(Curr))240continue;241242StringRef FileName = sys::path::filename(Curr);243// Scan all "public" headers.244if (FileName.contains("Headers")) {245if (Error Err = scanHeaders(Curr, Framework, HeaderType::Public, Curr))246return Err;247continue;248}249// Scan all "private" headers.250if (FileName.contains("PrivateHeaders")) {251if (Error Err = scanHeaders(Curr, Framework, HeaderType::Private, Curr))252return Err;253continue;254}255// Scan sub frameworks.256if (FileName.contains("Frameworks")) {257if (Error Err = scanSubFrameworksDirectory(Curr, Framework.SubFrameworks))258return Err;259continue;260}261// Check for versioned frameworks.262if (FileName.contains("Versions")) {263if (Error Err = scanFrameworkVersionsDirectory(Curr, Framework))264return Err;265continue;266}267}268269return Error::success();270}271272llvm::Error DirectoryScanner::scanForFrameworks(StringRef Directory) {273RootPath = "";274275// Expect a certain directory structure and naming convention to find276// frameworks.277static const char *SubDirectories[] = {"System/Library/Frameworks/",278"System/Library/PrivateFrameworks/"};279280// Check if the directory is already a framework.281if (isFramework(Directory)) {282Library &Framework = getOrCreateLibrary(Directory, Libraries);283if (Error Err = scanFrameworkDirectory(Directory, Framework))284return Err;285return Error::success();286}287288// Check known sub-directory locations.289for (const auto *SubDir : SubDirectories) {290SmallString<PATH_MAX> Path(Directory);291sys::path::append(Path, SubDir);292293if (Error Err = scanMultipleFrameworks(Path, Libraries))294return Err;295}296297return Error::success();298}299} // namespace clang::installapi300301302