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