1*0fca6ea1SDimitry Andric //===- Frontend.cpp ---------------------------------------------*- C++ -*-===// 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/Frontend.h" 10*0fca6ea1SDimitry Andric #include "clang/AST/Availability.h" 11*0fca6ea1SDimitry Andric #include "clang/InstallAPI/FrontendRecords.h" 12*0fca6ea1SDimitry Andric #include "llvm/ADT/SmallString.h" 13*0fca6ea1SDimitry Andric #include "llvm/ADT/StringRef.h" 14*0fca6ea1SDimitry Andric 15*0fca6ea1SDimitry Andric using namespace llvm; 16*0fca6ea1SDimitry Andric using namespace llvm::MachO; 17*0fca6ea1SDimitry Andric 18*0fca6ea1SDimitry Andric namespace clang::installapi { 19*0fca6ea1SDimitry Andric std::pair<GlobalRecord *, FrontendAttrs *> FrontendRecordsSlice::addGlobal( 20*0fca6ea1SDimitry Andric StringRef Name, RecordLinkage Linkage, GlobalRecord::Kind GV, 21*0fca6ea1SDimitry Andric const clang::AvailabilityInfo Avail, const Decl *D, const HeaderType Access, 22*0fca6ea1SDimitry Andric SymbolFlags Flags, bool Inlined) { 23*0fca6ea1SDimitry Andric 24*0fca6ea1SDimitry Andric GlobalRecord *GR = 25*0fca6ea1SDimitry Andric llvm::MachO::RecordsSlice::addGlobal(Name, Linkage, GV, Flags, Inlined); 26*0fca6ea1SDimitry Andric auto Result = FrontendRecords.insert( 27*0fca6ea1SDimitry Andric {GR, FrontendAttrs{Avail, D, D->getLocation(), Access}}); 28*0fca6ea1SDimitry Andric return {GR, &(Result.first->second)}; 29*0fca6ea1SDimitry Andric } 30*0fca6ea1SDimitry Andric 31*0fca6ea1SDimitry Andric std::pair<ObjCInterfaceRecord *, FrontendAttrs *> 32*0fca6ea1SDimitry Andric FrontendRecordsSlice::addObjCInterface(StringRef Name, RecordLinkage Linkage, 33*0fca6ea1SDimitry Andric const clang::AvailabilityInfo Avail, 34*0fca6ea1SDimitry Andric const Decl *D, HeaderType Access, 35*0fca6ea1SDimitry Andric bool IsEHType) { 36*0fca6ea1SDimitry Andric ObjCIFSymbolKind SymType = 37*0fca6ea1SDimitry Andric ObjCIFSymbolKind::Class | ObjCIFSymbolKind::MetaClass; 38*0fca6ea1SDimitry Andric if (IsEHType) 39*0fca6ea1SDimitry Andric SymType |= ObjCIFSymbolKind::EHType; 40*0fca6ea1SDimitry Andric 41*0fca6ea1SDimitry Andric ObjCInterfaceRecord *ObjCR = 42*0fca6ea1SDimitry Andric llvm::MachO::RecordsSlice::addObjCInterface(Name, Linkage, SymType); 43*0fca6ea1SDimitry Andric auto Result = FrontendRecords.insert( 44*0fca6ea1SDimitry Andric {ObjCR, FrontendAttrs{Avail, D, D->getLocation(), Access}}); 45*0fca6ea1SDimitry Andric return {ObjCR, &(Result.first->second)}; 46*0fca6ea1SDimitry Andric } 47*0fca6ea1SDimitry Andric 48*0fca6ea1SDimitry Andric std::pair<ObjCCategoryRecord *, FrontendAttrs *> 49*0fca6ea1SDimitry Andric FrontendRecordsSlice::addObjCCategory(StringRef ClassToExtend, 50*0fca6ea1SDimitry Andric StringRef CategoryName, 51*0fca6ea1SDimitry Andric const clang::AvailabilityInfo Avail, 52*0fca6ea1SDimitry Andric const Decl *D, HeaderType Access) { 53*0fca6ea1SDimitry Andric ObjCCategoryRecord *ObjCR = 54*0fca6ea1SDimitry Andric llvm::MachO::RecordsSlice::addObjCCategory(ClassToExtend, CategoryName); 55*0fca6ea1SDimitry Andric auto Result = FrontendRecords.insert( 56*0fca6ea1SDimitry Andric {ObjCR, FrontendAttrs{Avail, D, D->getLocation(), Access}}); 57*0fca6ea1SDimitry Andric return {ObjCR, &(Result.first->second)}; 58*0fca6ea1SDimitry Andric } 59*0fca6ea1SDimitry Andric 60*0fca6ea1SDimitry Andric std::pair<ObjCIVarRecord *, FrontendAttrs *> FrontendRecordsSlice::addObjCIVar( 61*0fca6ea1SDimitry Andric ObjCContainerRecord *Container, StringRef IvarName, RecordLinkage Linkage, 62*0fca6ea1SDimitry Andric const clang::AvailabilityInfo Avail, const Decl *D, HeaderType Access, 63*0fca6ea1SDimitry Andric const clang::ObjCIvarDecl::AccessControl AC) { 64*0fca6ea1SDimitry Andric // If the decl otherwise would have been exported, check their access control. 65*0fca6ea1SDimitry Andric // Ivar's linkage is also determined by this. 66*0fca6ea1SDimitry Andric if ((Linkage == RecordLinkage::Exported) && 67*0fca6ea1SDimitry Andric ((AC == ObjCIvarDecl::Private) || (AC == ObjCIvarDecl::Package))) 68*0fca6ea1SDimitry Andric Linkage = RecordLinkage::Internal; 69*0fca6ea1SDimitry Andric ObjCIVarRecord *ObjCR = 70*0fca6ea1SDimitry Andric llvm::MachO::RecordsSlice::addObjCIVar(Container, IvarName, Linkage); 71*0fca6ea1SDimitry Andric auto Result = FrontendRecords.insert( 72*0fca6ea1SDimitry Andric {ObjCR, FrontendAttrs{Avail, D, D->getLocation(), Access}}); 73*0fca6ea1SDimitry Andric 74*0fca6ea1SDimitry Andric return {ObjCR, &(Result.first->second)}; 75*0fca6ea1SDimitry Andric } 76*0fca6ea1SDimitry Andric 77*0fca6ea1SDimitry Andric std::optional<HeaderType> 78*0fca6ea1SDimitry Andric InstallAPIContext::findAndRecordFile(const FileEntry *FE, 79*0fca6ea1SDimitry Andric const Preprocessor &PP) { 80*0fca6ea1SDimitry Andric if (!FE) 81*0fca6ea1SDimitry Andric return std::nullopt; 82*0fca6ea1SDimitry Andric 83*0fca6ea1SDimitry Andric // Check if header has been looked up already and whether it is something 84*0fca6ea1SDimitry Andric // installapi should use. 85*0fca6ea1SDimitry Andric auto It = KnownFiles.find(FE); 86*0fca6ea1SDimitry Andric if (It != KnownFiles.end()) { 87*0fca6ea1SDimitry Andric if (It->second != HeaderType::Unknown) 88*0fca6ea1SDimitry Andric return It->second; 89*0fca6ea1SDimitry Andric else 90*0fca6ea1SDimitry Andric return std::nullopt; 91*0fca6ea1SDimitry Andric } 92*0fca6ea1SDimitry Andric 93*0fca6ea1SDimitry Andric // If file was not found, search by how the header was 94*0fca6ea1SDimitry Andric // included. This is primarily to resolve headers found 95*0fca6ea1SDimitry Andric // in a different location than what passed directly as input. 96*0fca6ea1SDimitry Andric StringRef IncludeName = PP.getHeaderSearchInfo().getIncludeNameForHeader(FE); 97*0fca6ea1SDimitry Andric auto BackupIt = KnownIncludes.find(IncludeName.str()); 98*0fca6ea1SDimitry Andric if (BackupIt != KnownIncludes.end()) { 99*0fca6ea1SDimitry Andric KnownFiles[FE] = BackupIt->second; 100*0fca6ea1SDimitry Andric return BackupIt->second; 101*0fca6ea1SDimitry Andric } 102*0fca6ea1SDimitry Andric 103*0fca6ea1SDimitry Andric // Record that the file was found to avoid future string searches for the 104*0fca6ea1SDimitry Andric // same file. 105*0fca6ea1SDimitry Andric KnownFiles.insert({FE, HeaderType::Unknown}); 106*0fca6ea1SDimitry Andric return std::nullopt; 107*0fca6ea1SDimitry Andric } 108*0fca6ea1SDimitry Andric 109*0fca6ea1SDimitry Andric void InstallAPIContext::addKnownHeader(const HeaderFile &H) { 110*0fca6ea1SDimitry Andric auto FE = FM->getFile(H.getPath()); 111*0fca6ea1SDimitry Andric if (!FE) 112*0fca6ea1SDimitry Andric return; // File does not exist. 113*0fca6ea1SDimitry Andric KnownFiles[*FE] = H.getType(); 114*0fca6ea1SDimitry Andric 115*0fca6ea1SDimitry Andric if (!H.useIncludeName()) 116*0fca6ea1SDimitry Andric return; 117*0fca6ea1SDimitry Andric 118*0fca6ea1SDimitry Andric KnownIncludes[H.getIncludeName()] = H.getType(); 119*0fca6ea1SDimitry Andric } 120*0fca6ea1SDimitry Andric 121*0fca6ea1SDimitry Andric static StringRef getFileExtension(clang::Language Lang) { 122*0fca6ea1SDimitry Andric switch (Lang) { 123*0fca6ea1SDimitry Andric default: 124*0fca6ea1SDimitry Andric llvm_unreachable("Unexpected language option."); 125*0fca6ea1SDimitry Andric case clang::Language::C: 126*0fca6ea1SDimitry Andric return ".c"; 127*0fca6ea1SDimitry Andric case clang::Language::CXX: 128*0fca6ea1SDimitry Andric return ".cpp"; 129*0fca6ea1SDimitry Andric case clang::Language::ObjC: 130*0fca6ea1SDimitry Andric return ".m"; 131*0fca6ea1SDimitry Andric case clang::Language::ObjCXX: 132*0fca6ea1SDimitry Andric return ".mm"; 133*0fca6ea1SDimitry Andric } 134*0fca6ea1SDimitry Andric } 135*0fca6ea1SDimitry Andric 136*0fca6ea1SDimitry Andric std::unique_ptr<MemoryBuffer> createInputBuffer(InstallAPIContext &Ctx) { 137*0fca6ea1SDimitry Andric assert(Ctx.Type != HeaderType::Unknown && 138*0fca6ea1SDimitry Andric "unexpected access level for parsing"); 139*0fca6ea1SDimitry Andric SmallString<4096> Contents; 140*0fca6ea1SDimitry Andric raw_svector_ostream OS(Contents); 141*0fca6ea1SDimitry Andric for (const HeaderFile &H : Ctx.InputHeaders) { 142*0fca6ea1SDimitry Andric if (H.isExcluded()) 143*0fca6ea1SDimitry Andric continue; 144*0fca6ea1SDimitry Andric if (H.getType() != Ctx.Type) 145*0fca6ea1SDimitry Andric continue; 146*0fca6ea1SDimitry Andric if (Ctx.LangMode == Language::C || Ctx.LangMode == Language::CXX) 147*0fca6ea1SDimitry Andric OS << "#include "; 148*0fca6ea1SDimitry Andric else 149*0fca6ea1SDimitry Andric OS << "#import "; 150*0fca6ea1SDimitry Andric if (H.useIncludeName()) 151*0fca6ea1SDimitry Andric OS << "<" << H.getIncludeName() << ">\n"; 152*0fca6ea1SDimitry Andric else 153*0fca6ea1SDimitry Andric OS << "\"" << H.getPath() << "\"\n"; 154*0fca6ea1SDimitry Andric 155*0fca6ea1SDimitry Andric Ctx.addKnownHeader(H); 156*0fca6ea1SDimitry Andric } 157*0fca6ea1SDimitry Andric if (Contents.empty()) 158*0fca6ea1SDimitry Andric return nullptr; 159*0fca6ea1SDimitry Andric 160*0fca6ea1SDimitry Andric SmallString<64> BufferName( 161*0fca6ea1SDimitry Andric {"installapi-includes-", Ctx.Slice->getTriple().str(), "-", 162*0fca6ea1SDimitry Andric getName(Ctx.Type), getFileExtension(Ctx.LangMode)}); 163*0fca6ea1SDimitry Andric return llvm::MemoryBuffer::getMemBufferCopy(Contents, BufferName); 164*0fca6ea1SDimitry Andric } 165*0fca6ea1SDimitry Andric 166*0fca6ea1SDimitry Andric std::string findLibrary(StringRef InstallName, FileManager &FM, 167*0fca6ea1SDimitry Andric ArrayRef<std::string> FrameworkSearchPaths, 168*0fca6ea1SDimitry Andric ArrayRef<std::string> LibrarySearchPaths, 169*0fca6ea1SDimitry Andric ArrayRef<std::string> SearchPaths) { 170*0fca6ea1SDimitry Andric auto getLibrary = 171*0fca6ea1SDimitry Andric [&](const StringRef FullPath) -> std::optional<std::string> { 172*0fca6ea1SDimitry Andric // Prefer TextAPI files when possible. 173*0fca6ea1SDimitry Andric SmallString<PATH_MAX> TextAPIFilePath = FullPath; 174*0fca6ea1SDimitry Andric replace_extension(TextAPIFilePath, ".tbd"); 175*0fca6ea1SDimitry Andric 176*0fca6ea1SDimitry Andric if (FM.getOptionalFileRef(TextAPIFilePath)) 177*0fca6ea1SDimitry Andric return std::string(TextAPIFilePath); 178*0fca6ea1SDimitry Andric 179*0fca6ea1SDimitry Andric if (FM.getOptionalFileRef(FullPath)) 180*0fca6ea1SDimitry Andric return std::string(FullPath); 181*0fca6ea1SDimitry Andric 182*0fca6ea1SDimitry Andric return std::nullopt; 183*0fca6ea1SDimitry Andric }; 184*0fca6ea1SDimitry Andric 185*0fca6ea1SDimitry Andric const StringRef Filename = sys::path::filename(InstallName); 186*0fca6ea1SDimitry Andric const bool IsFramework = sys::path::parent_path(InstallName) 187*0fca6ea1SDimitry Andric .ends_with((Filename + ".framework").str()); 188*0fca6ea1SDimitry Andric if (IsFramework) { 189*0fca6ea1SDimitry Andric for (const StringRef Path : FrameworkSearchPaths) { 190*0fca6ea1SDimitry Andric SmallString<PATH_MAX> FullPath(Path); 191*0fca6ea1SDimitry Andric sys::path::append(FullPath, Filename + StringRef(".framework"), Filename); 192*0fca6ea1SDimitry Andric if (auto LibOrNull = getLibrary(FullPath)) 193*0fca6ea1SDimitry Andric return *LibOrNull; 194*0fca6ea1SDimitry Andric } 195*0fca6ea1SDimitry Andric } else { 196*0fca6ea1SDimitry Andric // Copy Apple's linker behavior: If this is a .dylib inside a framework, do 197*0fca6ea1SDimitry Andric // not search -L paths. 198*0fca6ea1SDimitry Andric bool IsEmbeddedDylib = (sys::path::extension(InstallName) == ".dylib") && 199*0fca6ea1SDimitry Andric InstallName.contains(".framework/"); 200*0fca6ea1SDimitry Andric if (!IsEmbeddedDylib) { 201*0fca6ea1SDimitry Andric for (const StringRef Path : LibrarySearchPaths) { 202*0fca6ea1SDimitry Andric SmallString<PATH_MAX> FullPath(Path); 203*0fca6ea1SDimitry Andric sys::path::append(FullPath, Filename); 204*0fca6ea1SDimitry Andric if (auto LibOrNull = getLibrary(FullPath)) 205*0fca6ea1SDimitry Andric return *LibOrNull; 206*0fca6ea1SDimitry Andric } 207*0fca6ea1SDimitry Andric } 208*0fca6ea1SDimitry Andric } 209*0fca6ea1SDimitry Andric 210*0fca6ea1SDimitry Andric for (const StringRef Path : SearchPaths) { 211*0fca6ea1SDimitry Andric SmallString<PATH_MAX> FullPath(Path); 212*0fca6ea1SDimitry Andric sys::path::append(FullPath, InstallName); 213*0fca6ea1SDimitry Andric if (auto LibOrNull = getLibrary(FullPath)) 214*0fca6ea1SDimitry Andric return *LibOrNull; 215*0fca6ea1SDimitry Andric } 216*0fca6ea1SDimitry Andric 217*0fca6ea1SDimitry Andric return {}; 218*0fca6ea1SDimitry Andric } 219*0fca6ea1SDimitry Andric 220*0fca6ea1SDimitry Andric } // namespace clang::installapi 221