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