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 {
addGlobal(StringRef Name,RecordLinkage Linkage,GlobalRecord::Kind GV,const clang::AvailabilityInfo Avail,const Decl * D,const HeaderType Access,SymbolFlags Flags,bool Inlined)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 *>
addObjCInterface(StringRef Name,RecordLinkage Linkage,const clang::AvailabilityInfo Avail,const Decl * D,HeaderType Access,bool IsEHType)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 *>
addObjCCategory(StringRef ClassToExtend,StringRef CategoryName,const clang::AvailabilityInfo Avail,const Decl * D,HeaderType Access)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
addObjCIVar(ObjCContainerRecord * Container,StringRef IvarName,RecordLinkage Linkage,const clang::AvailabilityInfo Avail,const Decl * D,HeaderType Access,const clang::ObjCIvarDecl::AccessControl AC)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>
findAndRecordFile(const FileEntry * FE,const Preprocessor & PP)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
addKnownHeader(const HeaderFile & H)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
getFileExtension(clang::Language Lang)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
createInputBuffer(InstallAPIContext & Ctx)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
findLibrary(StringRef InstallName,FileManager & FM,ArrayRef<std::string> FrameworkSearchPaths,ArrayRef<std::string> LibrarySearchPaths,ArrayRef<std::string> SearchPaths)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