1 //===- Frontend.cpp ---------------------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
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"
16 using namespace llvm::MachO
;
18 namespace clang::installapi
{
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
) {
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
)};
31 std::pair
<ObjCInterfaceRecord
*, FrontendAttrs
*>
32 FrontendRecordsSlice::addObjCInterface(StringRef Name
, RecordLinkage Linkage
,
33 const clang::AvailabilityInfo Avail
,
34 const Decl
*D
, HeaderType Access
,
36 ObjCIFSymbolKind SymType
=
37 ObjCIFSymbolKind::Class
| ObjCIFSymbolKind::MetaClass
;
39 SymType
|= ObjCIFSymbolKind::EHType
;
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
)};
48 std::pair
<ObjCCategoryRecord
*, FrontendAttrs
*>
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
)};
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
}});
74 return {ObjCR
, &(Result
.first
->second
)};
77 std::optional
<HeaderType
>
78 InstallAPIContext::findAndRecordFile(const FileEntry
*FE
,
79 const Preprocessor
&PP
) {
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
)
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
);
98 if (BackupIt
!= KnownIncludes
.end()) {
99 KnownFiles
[FE
] = BackupIt
->second
;
100 return BackupIt
->second
;
103 // Record that the file was found to avoid future string searches for the
105 KnownFiles
.insert({FE
, HeaderType::Unknown
});
109 void InstallAPIContext::addKnownHeader(const HeaderFile
&H
) {
110 auto FE
= FM
->getOptionalFileRef(H
.getPath());
112 return; // File does not exist.
113 KnownFiles
[*FE
] = H
.getType();
115 if (!H
.useIncludeName())
118 KnownIncludes
[H
.getIncludeName()] = H
.getType();
121 static StringRef
getFileExtension(clang::Language Lang
) {
124 llvm_unreachable("Unexpected language option.");
125 case clang::Language::C
:
127 case clang::Language::CXX
:
129 case clang::Language::ObjC
:
131 case clang::Language::ObjCXX
:
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
) {
144 if (H
.getType() != Ctx
.Type
)
146 if (Ctx
.LangMode
== Language::C
|| Ctx
.LangMode
== Language::CXX
)
150 if (H
.useIncludeName())
151 OS
<< "<" << H
.getIncludeName() << ">\n";
153 OS
<< "\"" << H
.getPath() << "\"\n";
155 Ctx
.addKnownHeader(H
);
157 if (Contents
.empty())
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
);
166 std::string
findLibrary(StringRef InstallName
, FileManager
&FM
,
167 ArrayRef
<std::string
> FrameworkSearchPaths
,
168 ArrayRef
<std::string
> LibrarySearchPaths
,
169 ArrayRef
<std::string
> SearchPaths
) {
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");
176 if (FM
.getOptionalFileRef(TextAPIFilePath
))
177 return std::string(TextAPIFilePath
);
179 if (FM
.getOptionalFileRef(FullPath
))
180 return std::string(FullPath
);
185 const StringRef Filename
= sys::path::filename(InstallName
);
186 const bool IsFramework
= sys::path::parent_path(InstallName
)
187 .ends_with((Filename
+ ".framework").str());
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
))
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
))
210 for (const StringRef Path
: SearchPaths
) {
211 SmallString
<PATH_MAX
> FullPath(Path
);
212 sys::path::append(FullPath
, InstallName
);
213 if (auto LibOrNull
= getLibrary(FullPath
))
220 } // namespace clang::installapi