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