xref: /llvm-project/clang/lib/InstallAPI/Frontend.cpp (revision c714f928b2f9ab3dd481f272a2aa72b83fd0562e)
1c6cbf81cSCyndy Ishida //===- Frontend.cpp ---------------------------------------------*- C++ -*-===//
2c6cbf81cSCyndy Ishida //
3c6cbf81cSCyndy Ishida // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4c6cbf81cSCyndy Ishida // See https://llvm.org/LICENSE.txt for license information.
5c6cbf81cSCyndy Ishida // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6c6cbf81cSCyndy Ishida //
7c6cbf81cSCyndy Ishida //===----------------------------------------------------------------------===//
8c6cbf81cSCyndy Ishida 
9c6cbf81cSCyndy Ishida #include "clang/InstallAPI/Frontend.h"
10c6cbf81cSCyndy Ishida #include "clang/AST/Availability.h"
11a38b7a43SCyndy Ishida #include "clang/InstallAPI/FrontendRecords.h"
12c6cbf81cSCyndy Ishida #include "llvm/ADT/SmallString.h"
13c6cbf81cSCyndy Ishida #include "llvm/ADT/StringRef.h"
14c6cbf81cSCyndy Ishida 
15c6cbf81cSCyndy Ishida using namespace llvm;
16c6cbf81cSCyndy Ishida using namespace llvm::MachO;
17c6cbf81cSCyndy Ishida 
18c6cbf81cSCyndy Ishida namespace clang::installapi {
19f2794cceSCyndy Ishida std::pair<GlobalRecord *, FrontendAttrs *> FrontendRecordsSlice::addGlobal(
2017ede03aSCyndy Ishida     StringRef Name, RecordLinkage Linkage, GlobalRecord::Kind GV,
2117ede03aSCyndy Ishida     const clang::AvailabilityInfo Avail, const Decl *D, const HeaderType Access,
2250ae8a2aSCyndy Ishida     SymbolFlags Flags, bool Inlined) {
2317ede03aSCyndy Ishida 
24f2794cceSCyndy Ishida   GlobalRecord *GR =
2550ae8a2aSCyndy Ishida       llvm::MachO::RecordsSlice::addGlobal(Name, Linkage, GV, Flags, Inlined);
26f04452deSCyndy Ishida   auto Result = FrontendRecords.insert(
27f04452deSCyndy Ishida       {GR, FrontendAttrs{Avail, D, D->getLocation(), Access}});
28f2794cceSCyndy Ishida   return {GR, &(Result.first->second)};
2917ede03aSCyndy Ishida }
3017ede03aSCyndy Ishida 
31f2794cceSCyndy Ishida std::pair<ObjCInterfaceRecord *, FrontendAttrs *>
32f2794cceSCyndy Ishida FrontendRecordsSlice::addObjCInterface(StringRef Name, RecordLinkage Linkage,
33f2794cceSCyndy Ishida                                        const clang::AvailabilityInfo Avail,
34f2794cceSCyndy Ishida                                        const Decl *D, HeaderType Access,
35f2794cceSCyndy Ishida                                        bool IsEHType) {
3617ede03aSCyndy Ishida   ObjCIFSymbolKind SymType =
3717ede03aSCyndy Ishida       ObjCIFSymbolKind::Class | ObjCIFSymbolKind::MetaClass;
3817ede03aSCyndy Ishida   if (IsEHType)
3917ede03aSCyndy Ishida     SymType |= ObjCIFSymbolKind::EHType;
40f2794cceSCyndy Ishida 
41f2794cceSCyndy Ishida   ObjCInterfaceRecord *ObjCR =
4217ede03aSCyndy Ishida       llvm::MachO::RecordsSlice::addObjCInterface(Name, Linkage, SymType);
43f04452deSCyndy Ishida   auto Result = FrontendRecords.insert(
44f04452deSCyndy Ishida       {ObjCR, FrontendAttrs{Avail, D, D->getLocation(), Access}});
45f2794cceSCyndy Ishida   return {ObjCR, &(Result.first->second)};
4617ede03aSCyndy Ishida }
4717ede03aSCyndy Ishida 
48f2794cceSCyndy Ishida std::pair<ObjCCategoryRecord *, FrontendAttrs *>
49f2794cceSCyndy Ishida FrontendRecordsSlice::addObjCCategory(StringRef ClassToExtend,
50f2794cceSCyndy Ishida                                       StringRef CategoryName,
51f2794cceSCyndy Ishida                                       const clang::AvailabilityInfo Avail,
52f2794cceSCyndy Ishida                                       const Decl *D, HeaderType Access) {
53f2794cceSCyndy Ishida   ObjCCategoryRecord *ObjCR =
5410ccde30SCyndy Ishida       llvm::MachO::RecordsSlice::addObjCCategory(ClassToExtend, CategoryName);
55f04452deSCyndy Ishida   auto Result = FrontendRecords.insert(
56f04452deSCyndy Ishida       {ObjCR, FrontendAttrs{Avail, D, D->getLocation(), Access}});
57f2794cceSCyndy Ishida   return {ObjCR, &(Result.first->second)};
5810ccde30SCyndy Ishida }
5910ccde30SCyndy Ishida 
60f2794cceSCyndy Ishida std::pair<ObjCIVarRecord *, FrontendAttrs *> FrontendRecordsSlice::addObjCIVar(
6110ccde30SCyndy Ishida     ObjCContainerRecord *Container, StringRef IvarName, RecordLinkage Linkage,
6210ccde30SCyndy Ishida     const clang::AvailabilityInfo Avail, const Decl *D, HeaderType Access,
6310ccde30SCyndy Ishida     const clang::ObjCIvarDecl::AccessControl AC) {
6410ccde30SCyndy Ishida   // If the decl otherwise would have been exported, check their access control.
6510ccde30SCyndy Ishida   // Ivar's linkage is also determined by this.
6610ccde30SCyndy Ishida   if ((Linkage == RecordLinkage::Exported) &&
6710ccde30SCyndy Ishida       ((AC == ObjCIvarDecl::Private) || (AC == ObjCIvarDecl::Package)))
6810ccde30SCyndy Ishida     Linkage = RecordLinkage::Internal;
69f2794cceSCyndy Ishida   ObjCIVarRecord *ObjCR =
7010ccde30SCyndy Ishida       llvm::MachO::RecordsSlice::addObjCIVar(Container, IvarName, Linkage);
71f04452deSCyndy Ishida   auto Result = FrontendRecords.insert(
72f04452deSCyndy Ishida       {ObjCR, FrontendAttrs{Avail, D, D->getLocation(), Access}});
7310ccde30SCyndy Ishida 
74f2794cceSCyndy Ishida   return {ObjCR, &(Result.first->second)};
7510ccde30SCyndy Ishida }
7610ccde30SCyndy Ishida 
7717ede03aSCyndy Ishida std::optional<HeaderType>
7817ede03aSCyndy Ishida InstallAPIContext::findAndRecordFile(const FileEntry *FE,
7917ede03aSCyndy Ishida                                      const Preprocessor &PP) {
8017ede03aSCyndy Ishida   if (!FE)
8117ede03aSCyndy Ishida     return std::nullopt;
8217ede03aSCyndy Ishida 
8317ede03aSCyndy Ishida   // Check if header has been looked up already and whether it is something
8417ede03aSCyndy Ishida   // installapi should use.
8517ede03aSCyndy Ishida   auto It = KnownFiles.find(FE);
8617ede03aSCyndy Ishida   if (It != KnownFiles.end()) {
8717ede03aSCyndy Ishida     if (It->second != HeaderType::Unknown)
8817ede03aSCyndy Ishida       return It->second;
8917ede03aSCyndy Ishida     else
9017ede03aSCyndy Ishida       return std::nullopt;
9117ede03aSCyndy Ishida   }
9217ede03aSCyndy Ishida 
9317ede03aSCyndy Ishida   // If file was not found, search by how the header was
9417ede03aSCyndy Ishida   // included. This is primarily to resolve headers found
9517ede03aSCyndy Ishida   // in a different location than what passed directly as input.
9617ede03aSCyndy Ishida   StringRef IncludeName = PP.getHeaderSearchInfo().getIncludeNameForHeader(FE);
97*c714f928SKazu Hirata   auto BackupIt = KnownIncludes.find(IncludeName);
9817ede03aSCyndy Ishida   if (BackupIt != KnownIncludes.end()) {
9917ede03aSCyndy Ishida     KnownFiles[FE] = BackupIt->second;
10017ede03aSCyndy Ishida     return BackupIt->second;
10117ede03aSCyndy Ishida   }
10217ede03aSCyndy Ishida 
10317ede03aSCyndy Ishida   // Record that the file was found to avoid future string searches for the
10417ede03aSCyndy Ishida   // same file.
10517ede03aSCyndy Ishida   KnownFiles.insert({FE, HeaderType::Unknown});
10617ede03aSCyndy Ishida   return std::nullopt;
10717ede03aSCyndy Ishida }
10817ede03aSCyndy Ishida 
10917ede03aSCyndy Ishida void InstallAPIContext::addKnownHeader(const HeaderFile &H) {
110b1aea98cSJan Svoboda   auto FE = FM->getOptionalFileRef(H.getPath());
11117ede03aSCyndy Ishida   if (!FE)
11217ede03aSCyndy Ishida     return; // File does not exist.
11317ede03aSCyndy Ishida   KnownFiles[*FE] = H.getType();
11417ede03aSCyndy Ishida 
11517ede03aSCyndy Ishida   if (!H.useIncludeName())
11617ede03aSCyndy Ishida     return;
11717ede03aSCyndy Ishida 
11817ede03aSCyndy Ishida   KnownIncludes[H.getIncludeName()] = H.getType();
11917ede03aSCyndy Ishida }
12017ede03aSCyndy Ishida 
121c6cbf81cSCyndy Ishida static StringRef getFileExtension(clang::Language Lang) {
122c6cbf81cSCyndy Ishida   switch (Lang) {
123c6cbf81cSCyndy Ishida   default:
124c6cbf81cSCyndy Ishida     llvm_unreachable("Unexpected language option.");
125c6cbf81cSCyndy Ishida   case clang::Language::C:
126c6cbf81cSCyndy Ishida     return ".c";
127c6cbf81cSCyndy Ishida   case clang::Language::CXX:
128c6cbf81cSCyndy Ishida     return ".cpp";
129c6cbf81cSCyndy Ishida   case clang::Language::ObjC:
130c6cbf81cSCyndy Ishida     return ".m";
131c6cbf81cSCyndy Ishida   case clang::Language::ObjCXX:
132c6cbf81cSCyndy Ishida     return ".mm";
133c6cbf81cSCyndy Ishida   }
134c6cbf81cSCyndy Ishida }
135c6cbf81cSCyndy Ishida 
13617ede03aSCyndy Ishida std::unique_ptr<MemoryBuffer> createInputBuffer(InstallAPIContext &Ctx) {
137c6cbf81cSCyndy Ishida   assert(Ctx.Type != HeaderType::Unknown &&
138c6cbf81cSCyndy Ishida          "unexpected access level for parsing");
139c6cbf81cSCyndy Ishida   SmallString<4096> Contents;
140c6cbf81cSCyndy Ishida   raw_svector_ostream OS(Contents);
141c6cbf81cSCyndy Ishida   for (const HeaderFile &H : Ctx.InputHeaders) {
142487720fcSCyndy Ishida     if (H.isExcluded())
143487720fcSCyndy Ishida       continue;
144c6cbf81cSCyndy Ishida     if (H.getType() != Ctx.Type)
145c6cbf81cSCyndy Ishida       continue;
146c6cbf81cSCyndy Ishida     if (Ctx.LangMode == Language::C || Ctx.LangMode == Language::CXX)
147c6cbf81cSCyndy Ishida       OS << "#include ";
148c6cbf81cSCyndy Ishida     else
149c6cbf81cSCyndy Ishida       OS << "#import ";
150c6cbf81cSCyndy Ishida     if (H.useIncludeName())
1512c93beccSCyndy Ishida       OS << "<" << H.getIncludeName() << ">\n";
152c6cbf81cSCyndy Ishida     else
1532c93beccSCyndy Ishida       OS << "\"" << H.getPath() << "\"\n";
15417ede03aSCyndy Ishida 
15517ede03aSCyndy Ishida     Ctx.addKnownHeader(H);
156c6cbf81cSCyndy Ishida   }
157c6cbf81cSCyndy Ishida   if (Contents.empty())
158c6cbf81cSCyndy Ishida     return nullptr;
159c6cbf81cSCyndy Ishida 
1608116dfb8SCyndy Ishida   SmallString<64> BufferName(
1618116dfb8SCyndy Ishida       {"installapi-includes-", Ctx.Slice->getTriple().str(), "-",
1628116dfb8SCyndy Ishida        getName(Ctx.Type), getFileExtension(Ctx.LangMode)});
1638116dfb8SCyndy Ishida   return llvm::MemoryBuffer::getMemBufferCopy(Contents, BufferName);
164c6cbf81cSCyndy Ishida }
165c6cbf81cSCyndy Ishida 
16627b2d7d4SCyndy Ishida std::string findLibrary(StringRef InstallName, FileManager &FM,
16727b2d7d4SCyndy Ishida                         ArrayRef<std::string> FrameworkSearchPaths,
16827b2d7d4SCyndy Ishida                         ArrayRef<std::string> LibrarySearchPaths,
16927b2d7d4SCyndy Ishida                         ArrayRef<std::string> SearchPaths) {
17027b2d7d4SCyndy Ishida   auto getLibrary =
17127b2d7d4SCyndy Ishida       [&](const StringRef FullPath) -> std::optional<std::string> {
17227b2d7d4SCyndy Ishida     // Prefer TextAPI files when possible.
17327b2d7d4SCyndy Ishida     SmallString<PATH_MAX> TextAPIFilePath = FullPath;
17427b2d7d4SCyndy Ishida     replace_extension(TextAPIFilePath, ".tbd");
17527b2d7d4SCyndy Ishida 
17627b2d7d4SCyndy Ishida     if (FM.getOptionalFileRef(TextAPIFilePath))
17727b2d7d4SCyndy Ishida       return std::string(TextAPIFilePath);
17827b2d7d4SCyndy Ishida 
17927b2d7d4SCyndy Ishida     if (FM.getOptionalFileRef(FullPath))
18027b2d7d4SCyndy Ishida       return std::string(FullPath);
18127b2d7d4SCyndy Ishida 
18227b2d7d4SCyndy Ishida     return std::nullopt;
18327b2d7d4SCyndy Ishida   };
18427b2d7d4SCyndy Ishida 
18527b2d7d4SCyndy Ishida   const StringRef Filename = sys::path::filename(InstallName);
18627b2d7d4SCyndy Ishida   const bool IsFramework = sys::path::parent_path(InstallName)
18727b2d7d4SCyndy Ishida                                .ends_with((Filename + ".framework").str());
18827b2d7d4SCyndy Ishida   if (IsFramework) {
18927b2d7d4SCyndy Ishida     for (const StringRef Path : FrameworkSearchPaths) {
19027b2d7d4SCyndy Ishida       SmallString<PATH_MAX> FullPath(Path);
19127b2d7d4SCyndy Ishida       sys::path::append(FullPath, Filename + StringRef(".framework"), Filename);
19227b2d7d4SCyndy Ishida       if (auto LibOrNull = getLibrary(FullPath))
19327b2d7d4SCyndy Ishida         return *LibOrNull;
19427b2d7d4SCyndy Ishida     }
19527b2d7d4SCyndy Ishida   } else {
19627b2d7d4SCyndy Ishida     // Copy Apple's linker behavior: If this is a .dylib inside a framework, do
19727b2d7d4SCyndy Ishida     // not search -L paths.
19827b2d7d4SCyndy Ishida     bool IsEmbeddedDylib = (sys::path::extension(InstallName) == ".dylib") &&
19927b2d7d4SCyndy Ishida                            InstallName.contains(".framework/");
20027b2d7d4SCyndy Ishida     if (!IsEmbeddedDylib) {
20127b2d7d4SCyndy Ishida       for (const StringRef Path : LibrarySearchPaths) {
20227b2d7d4SCyndy Ishida         SmallString<PATH_MAX> FullPath(Path);
20327b2d7d4SCyndy Ishida         sys::path::append(FullPath, Filename);
20427b2d7d4SCyndy Ishida         if (auto LibOrNull = getLibrary(FullPath))
20527b2d7d4SCyndy Ishida           return *LibOrNull;
20627b2d7d4SCyndy Ishida       }
20727b2d7d4SCyndy Ishida     }
20827b2d7d4SCyndy Ishida   }
20927b2d7d4SCyndy Ishida 
21027b2d7d4SCyndy Ishida   for (const StringRef Path : SearchPaths) {
21127b2d7d4SCyndy Ishida     SmallString<PATH_MAX> FullPath(Path);
21227b2d7d4SCyndy Ishida     sys::path::append(FullPath, InstallName);
21327b2d7d4SCyndy Ishida     if (auto LibOrNull = getLibrary(FullPath))
21427b2d7d4SCyndy Ishida       return *LibOrNull;
21527b2d7d4SCyndy Ishida   }
21627b2d7d4SCyndy Ishida 
21727b2d7d4SCyndy Ishida   return {};
21827b2d7d4SCyndy Ishida }
21927b2d7d4SCyndy Ishida 
220c6cbf81cSCyndy Ishida } // namespace clang::installapi
221