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