blob: 9e8c60fbda3d004e994b3b680e7047a03c60b7df [file] [log] [blame]
//===- Frontend.cpp ---------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "clang/InstallAPI/Frontend.h"
#include "clang/AST/Availability.h"
#include "clang/InstallAPI/FrontendRecords.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
using namespace llvm;
using namespace llvm::MachO;
namespace clang::installapi {
std::pair<GlobalRecord *, FrontendAttrs *> FrontendRecordsSlice::addGlobal(
StringRef Name, RecordLinkage Linkage, GlobalRecord::Kind GV,
const clang::AvailabilityInfo Avail, const Decl *D, const HeaderType Access,
SymbolFlags Flags, bool Inlined) {
GlobalRecord *GR =
llvm::MachO::RecordsSlice::addGlobal(Name, Linkage, GV, Flags, Inlined);
auto Result = FrontendRecords.insert(
{GR, FrontendAttrs{Avail, D, D->getLocation(), Access}});
return {GR, &(Result.first->second)};
}
std::pair<ObjCInterfaceRecord *, FrontendAttrs *>
FrontendRecordsSlice::addObjCInterface(StringRef Name, RecordLinkage Linkage,
const clang::AvailabilityInfo Avail,
const Decl *D, HeaderType Access,
bool IsEHType) {
ObjCIFSymbolKind SymType =
ObjCIFSymbolKind::Class | ObjCIFSymbolKind::MetaClass;
if (IsEHType)
SymType |= ObjCIFSymbolKind::EHType;
ObjCInterfaceRecord *ObjCR =
llvm::MachO::RecordsSlice::addObjCInterface(Name, Linkage, SymType);
auto Result = FrontendRecords.insert(
{ObjCR, FrontendAttrs{Avail, D, D->getLocation(), Access}});
return {ObjCR, &(Result.first->second)};
}
std::pair<ObjCCategoryRecord *, FrontendAttrs *>
FrontendRecordsSlice::addObjCCategory(StringRef ClassToExtend,
StringRef CategoryName,
const clang::AvailabilityInfo Avail,
const Decl *D, HeaderType Access) {
ObjCCategoryRecord *ObjCR =
llvm::MachO::RecordsSlice::addObjCCategory(ClassToExtend, CategoryName);
auto Result = FrontendRecords.insert(
{ObjCR, FrontendAttrs{Avail, D, D->getLocation(), Access}});
return {ObjCR, &(Result.first->second)};
}
std::pair<ObjCIVarRecord *, FrontendAttrs *> FrontendRecordsSlice::addObjCIVar(
ObjCContainerRecord *Container, StringRef IvarName, RecordLinkage Linkage,
const clang::AvailabilityInfo Avail, const Decl *D, HeaderType Access,
const clang::ObjCIvarDecl::AccessControl AC) {
// If the decl otherwise would have been exported, check their access control.
// Ivar's linkage is also determined by this.
if ((Linkage == RecordLinkage::Exported) &&
((AC == ObjCIvarDecl::Private) || (AC == ObjCIvarDecl::Package)))
Linkage = RecordLinkage::Internal;
ObjCIVarRecord *ObjCR =
llvm::MachO::RecordsSlice::addObjCIVar(Container, IvarName, Linkage);
auto Result = FrontendRecords.insert(
{ObjCR, FrontendAttrs{Avail, D, D->getLocation(), Access}});
return {ObjCR, &(Result.first->second)};
}
std::optional<HeaderType>
InstallAPIContext::findAndRecordFile(const FileEntry *FE,
const Preprocessor &PP) {
if (!FE)
return std::nullopt;
// Check if header has been looked up already and whether it is something
// installapi should use.
auto It = KnownFiles.find(FE);
if (It != KnownFiles.end()) {
if (It->second != HeaderType::Unknown)
return It->second;
else
return std::nullopt;
}
// If file was not found, search by how the header was
// included. This is primarily to resolve headers found
// in a different location than what passed directly as input.
StringRef IncludeName = PP.getHeaderSearchInfo().getIncludeNameForHeader(FE);
auto BackupIt = KnownIncludes.find(IncludeName);
if (BackupIt != KnownIncludes.end()) {
KnownFiles[FE] = BackupIt->second;
return BackupIt->second;
}
// Record that the file was found to avoid future string searches for the
// same file.
KnownFiles.insert({FE, HeaderType::Unknown});
return std::nullopt;
}
void InstallAPIContext::addKnownHeader(const HeaderFile &H) {
auto FE = FM->getOptionalFileRef(H.getPath());
if (!FE)
return; // File does not exist.
KnownFiles[*FE] = H.getType();
if (!H.useIncludeName())
return;
KnownIncludes[H.getIncludeName()] = H.getType();
}
static StringRef getFileExtension(clang::Language Lang) {
switch (Lang) {
default:
llvm_unreachable("Unexpected language option.");
case clang::Language::C:
return ".c";
case clang::Language::CXX:
return ".cpp";
case clang::Language::ObjC:
return ".m";
case clang::Language::ObjCXX:
return ".mm";
}
}
std::unique_ptr<MemoryBuffer> createInputBuffer(InstallAPIContext &Ctx) {
assert(Ctx.Type != HeaderType::Unknown &&
"unexpected access level for parsing");
SmallString<4096> Contents;
raw_svector_ostream OS(Contents);
for (const HeaderFile &H : Ctx.InputHeaders) {
if (H.isExcluded())
continue;
if (H.getType() != Ctx.Type)
continue;
if (Ctx.LangMode == Language::C || Ctx.LangMode == Language::CXX)
OS << "#include ";
else
OS << "#import ";
if (H.useIncludeName())
OS << "<" << H.getIncludeName() << ">\n";
else
OS << "\"" << H.getPath() << "\"\n";
Ctx.addKnownHeader(H);
}
if (Contents.empty())
return nullptr;
SmallString<64> BufferName(
{"installapi-includes-", Ctx.Slice->getTriple().str(), "-",
getName(Ctx.Type), getFileExtension(Ctx.LangMode)});
return llvm::MemoryBuffer::getMemBufferCopy(Contents, BufferName);
}
std::string findLibrary(StringRef InstallName, FileManager &FM,
ArrayRef<std::string> FrameworkSearchPaths,
ArrayRef<std::string> LibrarySearchPaths,
ArrayRef<std::string> SearchPaths) {
auto getLibrary =
[&](const StringRef FullPath) -> std::optional<std::string> {
// Prefer TextAPI files when possible.
SmallString<PATH_MAX> TextAPIFilePath = FullPath;
replace_extension(TextAPIFilePath, ".tbd");
if (FM.getOptionalFileRef(TextAPIFilePath))
return std::string(TextAPIFilePath);
if (FM.getOptionalFileRef(FullPath))
return std::string(FullPath);
return std::nullopt;
};
const StringRef Filename = sys::path::filename(InstallName);
const bool IsFramework = sys::path::parent_path(InstallName)
.ends_with((Filename + ".framework").str());
if (IsFramework) {
for (const StringRef Path : FrameworkSearchPaths) {
SmallString<PATH_MAX> FullPath(Path);
sys::path::append(FullPath, Filename + StringRef(".framework"), Filename);
if (auto LibOrNull = getLibrary(FullPath))
return *LibOrNull;
}
} else {
// Copy Apple's linker behavior: If this is a .dylib inside a framework, do
// not search -L paths.
bool IsEmbeddedDylib = (sys::path::extension(InstallName) == ".dylib") &&
InstallName.contains(".framework/");
if (!IsEmbeddedDylib) {
for (const StringRef Path : LibrarySearchPaths) {
SmallString<PATH_MAX> FullPath(Path);
sys::path::append(FullPath, Filename);
if (auto LibOrNull = getLibrary(FullPath))
return *LibOrNull;
}
}
}
for (const StringRef Path : SearchPaths) {
SmallString<PATH_MAX> FullPath(Path);
sys::path::append(FullPath, InstallName);
if (auto LibOrNull = getLibrary(FullPath))
return *LibOrNull;
}
return {};
}
} // namespace clang::installapi