llvm-project/llvm/unittests/TextAPI/TextStubV5Tests.cpp

2455 lines
66 KiB
C++

//===-- TextStubV5Tests.cpp - TBD V5 File Test ----------------------------===//
//
// 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 "TextStubHelpers.h"
#include "llvm/TextAPI/InterfaceFile.h"
#include "llvm/TextAPI/TextAPIReader.h"
#include "llvm/TextAPI/TextAPIWriter.h"
#include "gtest/gtest.h"
#include <string>
#include <vector>
using namespace llvm;
using namespace llvm::MachO;
namespace TBDv5 {
TEST(TBDv5, ReadFile) {
static const char TBDv5File[] = R"({
"tapi_tbd_version": 5,
"main_library": {
"target_info": [
{
"target": "x86_64-macos",
"min_deployment": "10.14"
},
{
"target": "arm64-macos",
"min_deployment": "10.14"
},
{
"target": "arm64-maccatalyst",
"min_deployment": "12.1"
}
],
"flags": [
{
"targets": [
"x86_64-macos"
],
"attributes": [
"flat_namespace"
]
}
],
"install_names": [
{
"name": "/S/L/F/Foo.framework/Foo"
}
],
"current_versions": [
{
"version": "1.2"
}
],
"compatibility_versions": [
{ "version": "1.1" }
],
"rpaths": [
{
"targets": [
"x86_64-macos"
],
"paths": [
"@executable_path/.../Frameworks"
]
}
],
"parent_umbrellas": [
{
"umbrella": "System"
}
],
"allowable_clients": [
{
"clients": [
"ClientA",
"ClientB"
]
}
],
"reexported_libraries": [
{
"names": [
"/u/l/l/libfoo.dylib",
"/u/l/l/libbar.dylib"
]
}
],
"exported_symbols": [
{
"targets": [
"x86_64-macos",
"arm64-macos"
],
"data": {
"global": [
"_global"
],
"objc_class": [
"ClassA"
],
"weak": [],
"thread_local": []
},
"text": {
"global": [
"_func"
],
"weak": [],
"thread_local": []
}
},
{
"targets": [
"x86_64-macos"
],
"data": {
"global": [
"_globalVar"
],
"objc_class": [
"ClassA",
"ClassB",
"ClassData"
],
"objc_eh_type": [
"ClassA",
"ClassB"
],
"objc_ivar": [
"ClassA.ivar1",
"ClassA.ivar2",
"ClassC.ivar1"
]
},
"text": {
"global": [
"_funcFoo"
]
}
}
],
"reexported_symbols": [
{
"targets": [
"x86_64-macos",
"arm64-macos"
],
"data": {
"global": [
"_globalRe"
],
"objc_class": [
"ClassRexport"
]
},
"text": {
"global": [
"_funcA"
]
}
}
],
"undefined_symbols": [
{
"targets": [
"x86_64-macos"
],
"data": {
"global": [
"_globalBind"
],
"weak": [
"referenced_sym"
]
}
}
]
},
"libraries": []
})";
MemoryBufferRef InputBuf = MemoryBufferRef(TBDv5File, "Test.tbd");
Expected<FileType> ExpectedFT = TextAPIReader::canRead(InputBuf);
EXPECT_TRUE(!!ExpectedFT);
Expected<TBDFile> Result = TextAPIReader::get(InputBuf);
EXPECT_TRUE(!!Result);
TBDFile File = std::move(Result.get());
EXPECT_EQ(FileType::TBD_V5, File->getFileType());
EXPECT_EQ(*ExpectedFT, File->getFileType());
EXPECT_EQ(std::string("/S/L/F/Foo.framework/Foo"), File->getInstallName());
TargetList AllTargets = {
Target(AK_x86_64, PLATFORM_MACOS, VersionTuple(10, 14)),
Target(AK_arm64, PLATFORM_MACOS, VersionTuple(11, 0, 0)),
Target(AK_arm64, PLATFORM_MACCATALYST, VersionTuple(14, 0)),
};
std::set<Target> FileTargets{File->targets().begin(), File->targets().end()};
EXPECT_EQ(mapToPlatformSet(AllTargets), File->getPlatforms());
EXPECT_EQ(mapToArchitectureSet(AllTargets), File->getArchitectures());
EXPECT_EQ(FileTargets.size(), AllTargets.size());
for (const auto &Targ : AllTargets) {
auto FileTarg = FileTargets.find(Targ);
EXPECT_FALSE(FileTarg == FileTargets.end());
EXPECT_EQ(*FileTarg, Targ);
PackedVersion MD = Targ.MinDeployment;
PackedVersion FileMD = FileTarg->MinDeployment;
EXPECT_EQ(MD, FileMD);
}
EXPECT_EQ(PackedVersion(1, 2, 0), File->getCurrentVersion());
EXPECT_EQ(PackedVersion(1, 1, 0), File->getCompatibilityVersion());
EXPECT_TRUE(File->isApplicationExtensionSafe());
EXPECT_FALSE(File->isTwoLevelNamespace());
EXPECT_FALSE(File->isOSLibNotForSharedCache());
EXPECT_EQ(0U, File->documents().size());
InterfaceFileRef ClientA("ClientA", AllTargets);
InterfaceFileRef ClientB("ClientB", AllTargets);
EXPECT_EQ(2U, File->allowableClients().size());
EXPECT_EQ(ClientA, File->allowableClients().at(0));
EXPECT_EQ(ClientB, File->allowableClients().at(1));
InterfaceFileRef ReexportA("/u/l/l/libbar.dylib", AllTargets);
InterfaceFileRef ReexportB("/u/l/l/libfoo.dylib", AllTargets);
EXPECT_EQ(2U, File->reexportedLibraries().size());
EXPECT_EQ(ReexportA, File->reexportedLibraries().at(0));
EXPECT_EQ(ReexportB, File->reexportedLibraries().at(1));
TargetToAttr RPaths = {
{Target(AK_x86_64, PLATFORM_MACOS), "@executable_path/.../Frameworks"},
};
EXPECT_EQ(RPaths, File->rpaths());
TargetToAttr Umbrellas = {{Target(AK_x86_64, PLATFORM_MACOS), "System"},
{Target(AK_arm64, PLATFORM_MACOS), "System"},
{Target(AK_arm64, PLATFORM_MACCATALYST), "System"}};
EXPECT_EQ(Umbrellas, File->umbrellas());
ExportedSymbolSeq Exports, Reexports, Undefineds;
for (const auto *Sym : File->symbols()) {
TargetList SymTargets{Sym->targets().begin(), Sym->targets().end()};
ExportedSymbol Temp =
ExportedSymbol{Sym->getKind(),
std::string(Sym->getName()),
Sym->isWeakDefined() || Sym->isWeakReferenced(),
Sym->isThreadLocalValue(),
Sym->isData(),
SymTargets};
if (Sym->isUndefined())
Undefineds.emplace_back(std::move(Temp));
else
Sym->isReexported() ? Reexports.emplace_back(std::move(Temp))
: Exports.emplace_back(std::move(Temp));
}
llvm::sort(Exports);
llvm::sort(Reexports);
llvm::sort(Undefineds);
TargetList MacOSTargets = {Target(AK_x86_64, PLATFORM_MACOS),
Target(AK_arm64, PLATFORM_MACOS)};
std::vector<ExportedSymbol> ExpectedExportedSymbols = {
{EncodeKind::GlobalSymbol, "_func", false, false, false, MacOSTargets},
{EncodeKind::GlobalSymbol,
"_funcFoo",
false,
false,
false,
{Target(AK_x86_64, PLATFORM_MACOS)}},
{EncodeKind::GlobalSymbol, "_global", false, false, true, MacOSTargets},
{EncodeKind::GlobalSymbol,
"_globalVar",
false,
false,
true,
{Target(AK_x86_64, PLATFORM_MACOS)}},
{EncodeKind::ObjectiveCClass,
"ClassA",
false,
false,
true,
{Target(AK_x86_64, PLATFORM_MACOS)}},
{EncodeKind::ObjectiveCClass,
"ClassB",
false,
false,
true,
{Target(AK_x86_64, PLATFORM_MACOS)}},
{EncodeKind::ObjectiveCClass,
"ClassData",
false,
false,
true,
{Target(AK_x86_64, PLATFORM_MACOS)}},
{EncodeKind::ObjectiveCClassEHType,
"ClassA",
false,
false,
true,
{Target(AK_x86_64, PLATFORM_MACOS)}},
{EncodeKind::ObjectiveCClassEHType,
"ClassB",
false,
false,
true,
{Target(AK_x86_64, PLATFORM_MACOS)}},
{EncodeKind::ObjectiveCInstanceVariable,
"ClassA.ivar1",
false,
false,
true,
{Target(AK_x86_64, PLATFORM_MACOS)}},
{EncodeKind::ObjectiveCInstanceVariable,
"ClassA.ivar2",
false,
false,
true,
{Target(AK_x86_64, PLATFORM_MACOS)}},
{EncodeKind::ObjectiveCInstanceVariable,
"ClassC.ivar1",
false,
false,
true,
{Target(AK_x86_64, PLATFORM_MACOS)}},
};
std::vector<ExportedSymbol> ExpectedReexportedSymbols = {
{EncodeKind::GlobalSymbol, "_funcA", false, false, false, MacOSTargets},
{EncodeKind::GlobalSymbol, "_globalRe", false, false, true, MacOSTargets},
{EncodeKind::ObjectiveCClass, "ClassRexport", false, false, true,
MacOSTargets},
};
std::vector<ExportedSymbol> ExpectedUndefinedSymbols = {
{EncodeKind::GlobalSymbol,
"_globalBind",
false,
false,
true,
{Target(AK_x86_64, PLATFORM_MACOS)}},
{EncodeKind::GlobalSymbol,
"referenced_sym",
true,
false,
true,
{Target(AK_x86_64, PLATFORM_MACOS)}},
};
EXPECT_EQ(ExpectedExportedSymbols.size(), Exports.size());
EXPECT_EQ(ExpectedReexportedSymbols.size(), Reexports.size());
EXPECT_EQ(ExpectedUndefinedSymbols.size(), Undefineds.size());
EXPECT_TRUE(std::equal(Exports.begin(), Exports.end(),
std::begin(ExpectedExportedSymbols)));
EXPECT_TRUE(std::equal(Reexports.begin(), Reexports.end(),
std::begin(ExpectedReexportedSymbols)));
EXPECT_TRUE(std::equal(Undefineds.begin(), Undefineds.end(),
std::begin(ExpectedUndefinedSymbols)));
EXPECT_TRUE(
File->getSymbol(EncodeKind::GlobalSymbol, "_globalBind").has_value());
}
TEST(TBDv5, ReadMultipleTargets) {
static const char TBDv5File[] = R"({
"tapi_tbd_version": 5,
"main_library": {
"target_info": [
{
"target": "x86_64-macos",
"min_deployment": "10.14"
},
{
"target": "arm64-macos",
"min_deployment": "10.14"
},
{
"target": "arm64-maccatalyst",
"min_deployment": "12.1"
}
],
"install_names":[
{ "name":"/usr/lib/libFoo.dylib" }
],
"swift_abi":[ { "abi":8 } ],
"reexported_libraries": [
{
"targets": [ "x86_64-maccatalyst" ],
"names": [
"/u/l/l/libfoo.dylib",
"/u/l/l/libbar.dylib"
]
},
{
"targets": [ "arm64-maccatalyst" ],
"names": [ "/u/l/l/libArmOnly.dylib" ]
}
]
}
})";
Expected<TBDFile> Result =
TextAPIReader::get(MemoryBufferRef(TBDv5File, "Test.tbd"));
EXPECT_TRUE(!!Result);
TBDFile File = std::move(Result.get());
EXPECT_EQ(FileType::TBD_V5, File->getFileType());
EXPECT_EQ(std::string("/usr/lib/libFoo.dylib"), File->getInstallName());
EXPECT_TRUE(File->isApplicationExtensionSafe());
EXPECT_TRUE(File->isTwoLevelNamespace());
EXPECT_EQ(PackedVersion(1, 0, 0), File->getCurrentVersion());
EXPECT_EQ(PackedVersion(1, 0, 0), File->getCompatibilityVersion());
EXPECT_EQ(8U, File->getSwiftABIVersion());
TargetList AllTargets = {
Target(AK_x86_64, PLATFORM_MACOS, VersionTuple(10, 14)),
Target(AK_arm64, PLATFORM_MACOS, VersionTuple(10, 14)),
Target(AK_arm64, PLATFORM_MACCATALYST, VersionTuple(12, 1)),
};
EXPECT_EQ(mapToPlatformSet(AllTargets), File->getPlatforms());
EXPECT_EQ(mapToArchitectureSet(AllTargets), File->getArchitectures());
InterfaceFileRef ReexportA("/u/l/l/libArmOnly.dylib",
{Target(AK_arm64, PLATFORM_MACCATALYST)});
InterfaceFileRef ReexportB("/u/l/l/libbar.dylib",
{Target(AK_x86_64, PLATFORM_MACCATALYST)});
InterfaceFileRef ReexportC("/u/l/l/libfoo.dylib",
{Target(AK_x86_64, PLATFORM_MACCATALYST)});
EXPECT_EQ(3U, File->reexportedLibraries().size());
EXPECT_EQ(ReexportA, File->reexportedLibraries().at(0));
EXPECT_EQ(ReexportB, File->reexportedLibraries().at(1));
EXPECT_EQ(ReexportC, File->reexportedLibraries().at(2));
}
TEST(TBDv5, ReadMultipleDocuments) {
static const char TBDv5File[] = R"({
"tapi_tbd_version": 5,
"main_library": {
"target_info": [
{
"target": "armv7-ios",
"min_deployment": "11.0"
}
],
"install_names":[
{ "name":"/S/L/F/Foo.framework/Foo" }
],
"reexported_libraries": [
{ "names": ["/u/l/l/libfoo.dylib"] }
]
},
"libraries": [
{
"target_info": [
{
"target": "armv7-ios",
"min_deployment": "11.0"
}
],
"install_names":[
{ "name":"/u/l/l/libfoo.dylib" }
],
"flags":[
{ "attributes": ["not_app_extension_safe"] }
],
"exported_symbols": [
{
"data": {
"thread_local": [ "_globalVar" ],
"objc_class": [ "ClassData", "ClassA", "ClassB"],
"objc_eh_type": [ "ClassA", "ClassB" ]
},
"text": {
"global": [ "_funcFoo" ]
}
}
]
}
]})";
Expected<TBDFile> Result =
TextAPIReader::get(MemoryBufferRef(TBDv5File, "Test.tbd"));
EXPECT_TRUE(!!Result);
TBDFile File = std::move(Result.get());
EXPECT_EQ(FileType::TBD_V5, File->getFileType());
EXPECT_EQ(std::string("/S/L/F/Foo.framework/Foo"), File->getInstallName());
EXPECT_TRUE(File->isTwoLevelNamespace());
EXPECT_TRUE(File->isApplicationExtensionSafe());
TargetList Targets(File->targets().begin(), File->targets().end());
Target iOSTarget(AK_armv7, PLATFORM_IOS, VersionTuple(11, 0));
EXPECT_EQ(TargetList{iOSTarget}, Targets);
std::vector<const Symbol *> Symbols(File->symbols().begin(),
File->symbols().end());
EXPECT_EQ(0U, Symbols.size());
InterfaceFileRef Reexport("/u/l/l/libfoo.dylib", {iOSTarget});
EXPECT_EQ(1U, File->reexportedLibraries().size());
EXPECT_EQ(Reexport, File->reexportedLibraries().at(0));
// Check inlined library.
EXPECT_EQ(1U, File->documents().size());
TBDReexportFile Document = File->documents().front();
Targets = {Document->targets().begin(), Document->targets().end()};
EXPECT_EQ(TargetList{iOSTarget}, Targets);
EXPECT_EQ(std::string("/u/l/l/libfoo.dylib"), Document->getInstallName());
EXPECT_EQ(0U, Document->getSwiftABIVersion());
EXPECT_TRUE(Document->isTwoLevelNamespace());
EXPECT_FALSE(Document->isApplicationExtensionSafe());
ExportedSymbolSeq Exports;
for (const auto *Sym : Document->symbols())
Exports.emplace_back(
ExportedSymbol{Sym->getKind(),
std::string(Sym->getName()),
Sym->isWeakDefined() || Sym->isWeakReferenced(),
Sym->isThreadLocalValue(),
Sym->isData(),
{iOSTarget}});
llvm::sort(Exports);
ExportedSymbolSeq ExpectedExports = {
{EncodeKind::GlobalSymbol, "_funcFoo", false, false, false, {iOSTarget}},
{EncodeKind::GlobalSymbol, "_globalVar", false, true, true, {iOSTarget}},
{EncodeKind::ObjectiveCClass, "ClassA", false, false, true, {iOSTarget}},
{EncodeKind::ObjectiveCClass, "ClassB", false, false, true, {iOSTarget}},
{EncodeKind::ObjectiveCClass,
"ClassData",
false,
false,
true,
{iOSTarget}},
{EncodeKind::ObjectiveCClassEHType,
"ClassA",
false,
false,
true,
{iOSTarget}},
{EncodeKind::ObjectiveCClassEHType,
"ClassB",
false,
false,
true,
{iOSTarget}},
};
EXPECT_EQ(ExpectedExports.size(), Exports.size());
EXPECT_TRUE(
std::equal(Exports.begin(), Exports.end(), std::begin(ExpectedExports)));
}
TEST(TBDv5, WriteFile) {
static const char TBDv5File[] = R"({
"tapi_tbd_version": 5,
"main_library": {
"target_info": [
{
"target": "x86_64-macos",
"min_deployment": "10.14"
},
{
"target": "arm64-macos",
"min_deployment": "10.14"
},
{
"target": "arm64-maccatalyst",
"min_deployment": "12.1"
}
],
"install_names": [
{
"name": "@rpath/S/L/F/Foo.framework/Foo"
}
],
"current_versions": [
{
"version": "1.2"
}
],
"compatibility_versions": [
{ "version": "1.1" }
],
"flags": [
{
"attributes": [
"flat_namespace"
]
}
],
"rpaths": [
{
"targets": [
"x86_64-macos"
],
"paths": [
"@executable_path/.../Frameworks"
]
}
],
"parent_umbrellas": [
{
"umbrella": "System"
}
],
"allowable_clients": [
{
"clients": [
"ClientA",
"ClientB"
]
}
],
"reexported_libraries": [
{
"names": [
"/u/l/l/libfoo.dylib",
"/u/l/l/libbar.dylib"
]
}
],
"exported_symbols": [
{
"targets": [
"x86_64-macos",
"arm64-macos"
],
"data": {
"global": [
"_global"
],
"objc_class": [
"ClassA"
],
"weak": [],
"thread_local": []
},
"text": {
"global": [
"_func"
],
"weak": [],
"thread_local": []
}
},
{
"targets": [
"x86_64-macos"
],
"data": {
"global": [
"_globalVar"
],
"objc_class": [
"ClassA",
"ClassB",
"ClassData"
],
"objc_eh_type": [
"ClassA",
"ClassB"
],
"objc_ivar": [
"ClassA.ivar1",
"ClassA.ivar2",
"ClassC.ivar1"
]
},
"text": {
"global": [
"_funcFoo"
]
}
}
],
"reexported_symbols": [
{
"data": {
"global": [
"_globalRe"
],
"objc_class": [
"ClassRexport"
]
},
"text": {
"global": [
"_funcA"
]
}
}
],
"undefined_symbols": [
{
"targets": [
"x86_64-macos"
],
"data": {
"global": [
"_globalBind"
],
"weak": [
"referenced_sym"
]
}
}
]
}})";
InterfaceFile File;
File.setFileType(FileType::TBD_V5);
TargetList AllTargets = {
Target(AK_x86_64, PLATFORM_MACOS, VersionTuple(10, 14)),
Target(AK_arm64, PLATFORM_MACOS, VersionTuple(10, 14)),
Target(AK_arm64, PLATFORM_MACCATALYST, VersionTuple(12, 1)),
};
File.addTargets(AllTargets);
File.setInstallName("@rpath/S/L/F/Foo.framework/Foo");
File.setCurrentVersion(PackedVersion(1, 2, 0));
File.setCompatibilityVersion(PackedVersion(1, 1, 0));
File.addRPath("@executable_path/.../Frameworks", AllTargets[0]);
for (const auto &Targ : AllTargets) {
File.addParentUmbrella(Targ, "System");
File.addAllowableClient("ClientA", Targ);
File.addAllowableClient("ClientB", Targ);
File.addReexportedLibrary("/u/l/l/libfoo.dylib", Targ);
File.addReexportedLibrary("/u/l/l/libbar.dylib", Targ);
}
SymbolFlags Flags = SymbolFlags::None;
// Exports.
File.addSymbol(EncodeKind::GlobalSymbol, "_global",
{AllTargets[0], AllTargets[1]}, Flags | SymbolFlags::Data);
File.addSymbol(EncodeKind::GlobalSymbol, "_func",
{AllTargets[0], AllTargets[1]}, Flags | SymbolFlags::Text);
File.addSymbol(EncodeKind::ObjectiveCClass, "ClassA",
{AllTargets[0], AllTargets[1]}, Flags | SymbolFlags::Data);
File.addSymbol(EncodeKind::GlobalSymbol, "_funcFoo", {AllTargets[0]},
Flags | SymbolFlags::Text);
File.addSymbol(EncodeKind::GlobalSymbol, "_globalVar", {AllTargets[0]},
Flags | SymbolFlags::Data);
File.addSymbol(EncodeKind::ObjectiveCClass, "ClassData", {AllTargets[0]},
Flags | SymbolFlags::Data);
File.addSymbol(EncodeKind::ObjectiveCClassEHType, "ClassA", {AllTargets[0]},
Flags | SymbolFlags::Data);
File.addSymbol(EncodeKind::ObjectiveCClassEHType, "ClassB", {AllTargets[0]},
Flags | SymbolFlags::Data);
File.addSymbol(EncodeKind::ObjectiveCInstanceVariable, "ClassA.ivar1",
{AllTargets[0]}, Flags | SymbolFlags::Data);
File.addSymbol(EncodeKind::ObjectiveCInstanceVariable, "ClassA.ivar2",
{AllTargets[0]}, Flags | SymbolFlags::Data);
File.addSymbol(EncodeKind::ObjectiveCInstanceVariable, "ClassC.ivar1",
{AllTargets[0]}, Flags | SymbolFlags::Data);
// Reexports.
Flags = SymbolFlags::Rexported;
File.addSymbol(EncodeKind::GlobalSymbol, "_globalRe", AllTargets,
Flags | SymbolFlags::Data);
File.addSymbol(EncodeKind::GlobalSymbol, "_funcA", AllTargets,
Flags | SymbolFlags::Text);
File.addSymbol(EncodeKind::ObjectiveCClass, "ClassRexport", AllTargets,
Flags | SymbolFlags::Data);
// Undefineds.
Flags = SymbolFlags::Undefined;
File.addSymbol(EncodeKind::GlobalSymbol, "_globalBind", {AllTargets[0]},
Flags | SymbolFlags::Data);
File.addSymbol(EncodeKind::GlobalSymbol, "referenced_sym", {AllTargets[0]},
Flags | SymbolFlags::Data | SymbolFlags::WeakReferenced);
File.setTwoLevelNamespace(false);
File.setApplicationExtensionSafe(true);
// Write out file then process it back into IF and compare equality
// against TBDv5File.
SmallString<4096> Buffer;
raw_svector_ostream OS(Buffer);
Error Result = TextAPIWriter::writeToStream(OS, File);
EXPECT_FALSE(Result);
Expected<TBDFile> Input =
TextAPIReader::get(MemoryBufferRef(TBDv5File, "Input.tbd"));
EXPECT_TRUE(!!Input);
TBDFile InputFile = std::move(Input.get());
Expected<TBDFile> Output =
TextAPIReader::get(MemoryBufferRef(Buffer, "Output.tbd"));
EXPECT_TRUE(!!Output);
TBDFile OutputFile = std::move(Output.get());
EXPECT_EQ(*InputFile, *OutputFile);
}
TEST(TBDv5, WriteMultipleDocuments) {
static const char TBDv5File[] = R"({
"tapi_tbd_version": 5,
"main_library": {
"target_info": [
{
"target": "armv7-ios",
"min_deployment": "11.0"
}
],
"install_names":[
{ "name":"/S/L/F/Foo.framework/Foo" }
],
"reexported_libraries": [
{ "names": ["/u/l/l/libfoo.dylib"]
}
]
},
"libraries": [
{
"target_info": [
{
"target": "armv7-ios",
"min_deployment": "11.0"
},
{
"target": "armv7s-ios",
"min_deployment": "11.0"
}
],
"install_names":[
{ "name":"/u/l/l/libfoo.dylib" }
],
"current_versions": [
{
"version": "2.1.1"
}
],
"rpaths": [
{
"targets": [
"armv7-ios"
],
"paths": [
"@executable_path/.../Frameworks"
]
}],
"reexported_libraries": [ { "names": ["@rpath/libfoo.dylib"] } ],
"flags":[
{ "attributes": ["not_app_extension_safe"] }
],
"exported_symbols": [
{
"text": {
"global": [ "_funcFoo" ]
}
}
]
},
{
"target_info": [
{
"target": "armv7-ios",
"min_deployment": "11.0"
}
],
"install_names":[
{ "name":"@rpath/libfoo.dylib" }
],
"exported_symbols": [
{
"data": {
"global": [ "_varFooBaz" ]
}
}
]
}
]})";
InterfaceFile File;
File.setFileType(FileType::TBD_V5);
TargetList AllTargets = {
Target(AK_armv7, PLATFORM_IOS, VersionTuple(11, 0)),
Target(AK_armv7s, PLATFORM_IOS, VersionTuple(11, 0)),
};
File.setInstallName("/S/L/F/Foo.framework/Foo");
File.addTarget(AllTargets[0]);
File.setCurrentVersion(PackedVersion(1, 0, 0));
File.setCompatibilityVersion(PackedVersion(1, 0, 0));
File.addReexportedLibrary("/u/l/l/libfoo.dylib", AllTargets[0]);
File.setTwoLevelNamespace();
File.setApplicationExtensionSafe(true);
InterfaceFile NestedFile;
NestedFile.setFileType(FileType::TBD_V5);
NestedFile.setInstallName("/u/l/l/libfoo.dylib");
NestedFile.addTargets(AllTargets);
NestedFile.setCompatibilityVersion(PackedVersion(1, 0, 0));
NestedFile.setTwoLevelNamespace();
NestedFile.setApplicationExtensionSafe(false);
NestedFile.setCurrentVersion(PackedVersion(2, 1, 1));
NestedFile.addRPath("@executable_path/.../Frameworks", AllTargets[0]);
for (const auto &Targ : AllTargets)
NestedFile.addReexportedLibrary("@rpath/libfoo.dylib", Targ);
NestedFile.addSymbol(EncodeKind::GlobalSymbol, "_funcFoo", AllTargets,
SymbolFlags::Text);
File.addDocument(std::make_shared<InterfaceFile>(std::move(NestedFile)));
InterfaceFile NestedFileB;
NestedFileB.setFileType(FileType::TBD_V5);
NestedFileB.setInstallName("@rpath/libfoo.dylib");
NestedFileB.addTarget(AllTargets[0]);
NestedFileB.setCompatibilityVersion(PackedVersion(1, 0, 0));
NestedFileB.setCurrentVersion(PackedVersion(1, 0, 0));
NestedFileB.setTwoLevelNamespace();
NestedFileB.setApplicationExtensionSafe(true);
NestedFileB.addSymbol(EncodeKind::GlobalSymbol, "_varFooBaz", {AllTargets[0]},
SymbolFlags::Data);
File.addDocument(std::make_shared<InterfaceFile>(std::move(NestedFileB)));
// Write out file then process it back into IF and compare equality
// against TBDv5File.
SmallString<4096> Buffer;
raw_svector_ostream OS(Buffer);
Error Result = TextAPIWriter::writeToStream(OS, File, FileType::Invalid,
/*Compact=*/true);
EXPECT_FALSE(Result);
Expected<TBDFile> Input =
TextAPIReader::get(MemoryBufferRef(TBDv5File, "Input.tbd"));
EXPECT_TRUE(!!Input);
TBDFile InputFile = std::move(Input.get());
Expected<TBDFile> Output =
TextAPIReader::get(MemoryBufferRef(Buffer, "Output.tbd"));
EXPECT_TRUE(!!Output);
TBDFile OutputFile = std::move(Output.get());
EXPECT_EQ(*InputFile, *OutputFile);
}
TEST(TBDv5, Target_Simulator) {
static const char TBDv5File[] = R"({
"tapi_tbd_version": 5,
"main_library": {
"target_info": [
{
"target": "arm64-ios-simulator",
"min_deployment": "11.0"
},
{
"target": "x86_64-ios-simulator",
"min_deployment": "11.3"
}
],
"install_names":[
{ "name":"/S/L/F/Foo.framework/Foo" }
]
}})";
Expected<TBDFile> Result =
TextAPIReader::get(MemoryBufferRef(TBDv5File, "Test.tbd"));
EXPECT_TRUE(!!Result);
TBDFile File = std::move(Result.get());
EXPECT_EQ(FileType::TBD_V5, File->getFileType());
TargetList ExpectedTargets = {
Target(AK_x86_64, PLATFORM_IOSSIMULATOR, VersionTuple(11, 3)),
Target(AK_arm64, PLATFORM_IOSSIMULATOR, VersionTuple(14, 0)),
};
TargetList Targets{File->targets().begin(), File->targets().end()};
llvm::sort(Targets);
EXPECT_EQ(Targets, ExpectedTargets);
SmallString<4096> Buffer;
raw_svector_ostream OS(Buffer);
Error WriteResult = TextAPIWriter::writeToStream(OS, *File);
EXPECT_TRUE(!WriteResult);
Expected<TBDFile> Output =
TextAPIReader::get(MemoryBufferRef(Buffer, "Output.tbd"));
EXPECT_TRUE(!!Output);
TBDFile WriteResultFile = std::move(Output.get());
EXPECT_EQ(*File, *WriteResultFile);
}
TEST(TBDv5, Target_UnsupportedMinOS) {
static const char TBDv5File[] = R"({
"tapi_tbd_version": 5,
"main_library": {
"target_info": [
{
"target": "arm64-macos",
"min_deployment": "10.14"
},
{
"target": "x86_64-macos",
"min_deployment": "10.14"
}
],
"install_names":[
{ "name":"/S/L/F/Foo.framework/Foo" }
]
}})";
Expected<TBDFile> Result =
TextAPIReader::get(MemoryBufferRef(TBDv5File, "Test.tbd"));
EXPECT_TRUE(!!Result);
TBDFile File = std::move(Result.get());
EXPECT_EQ(FileType::TBD_V5, File->getFileType());
TargetList ExpectedTargets = {
Target(AK_x86_64, PLATFORM_MACOS, VersionTuple(10, 14)),
Target(AK_arm64, PLATFORM_MACOS, VersionTuple(11, 0)),
};
TargetList Targets{File->targets().begin(), File->targets().end()};
llvm::sort(Targets);
EXPECT_EQ(Targets, ExpectedTargets);
SmallString<4096> Buffer;
raw_svector_ostream OS(Buffer);
Error WriteResult = TextAPIWriter::writeToStream(OS, *File);
EXPECT_TRUE(!WriteResult);
Expected<TBDFile> Output =
TextAPIReader::get(MemoryBufferRef(Buffer, "Output.tbd"));
EXPECT_TRUE(!!Output);
TBDFile WriteResultFile = std::move(Output.get());
EXPECT_EQ(*File, *WriteResultFile);
}
TEST(TBDv5, MisspelledKey) {
static const char TBDv5File[] = R"({
"tapi_tbd_version": 5,
"main_library": {
"target_info": [
{
"target": "arm64-ios-simulator",
"min_deployment": "11.0"
}
],
"intall_names":[
{ "name":"/S/L/F/Foo.framework/Foo" }
]
}})";
Expected<TBDFile> Result =
TextAPIReader::get(MemoryBufferRef(TBDv5File, "Test.tbd"));
EXPECT_FALSE(!!Result);
std::string ErrorMessage = toString(Result.takeError());
EXPECT_EQ("invalid install_names section\n", ErrorMessage);
}
TEST(TBDv5, InvalidVersion) {
static const char TBDv5File[] = R"({
"tapi_tbd_version": 11,
"main_library": {
"target_info": [
{
"target": "arm64-ios-simulator",
"min_deployment": "11.0"
}
],
"install_names":[
{ "name":"/S/L/F/Foo.framework/Foo" }
]
}})";
Expected<TBDFile> Result =
TextAPIReader::get(MemoryBufferRef(TBDv5File, "Test.tbd"));
EXPECT_FALSE(!!Result);
std::string ErrorMessage = toString(Result.takeError());
EXPECT_EQ("invalid tapi_tbd_version section\n", ErrorMessage);
}
TEST(TBDv5, MissingRequiredKey) {
static const char TBDv5File[] = R"({
"main_library": {
"target_info": [
{
"target": "arm64-ios-simulator",
"min_deployment": "11.0"
}
],
"install_names":[
{ "name":"/S/L/F/Foo.framework/Foo" }
]
}})";
Expected<TBDFile> Result =
TextAPIReader::get(MemoryBufferRef(TBDv5File, "Test.tbd"));
EXPECT_FALSE(!!Result);
std::string ErrorMessage = toString(Result.takeError());
EXPECT_EQ("invalid tapi_tbd_version section\n", ErrorMessage);
}
TEST(TBDv5, InvalidSymbols) {
static const char TBDv5File[] = R"({
"tapi_tbd_version": 5,
"main_library": {
"target_info": [
{
"target": "arm64-driverkit",
"min_deployment": "11.0"
}
],
"install_names":[
{ "name":"/S/L/F/Foo.framework/Foo" }
],
"exported_symbols": [
{
"daa": {
"global": {
"weak": []
}
}
}
]
}})";
Expected<TBDFile> Result =
TextAPIReader::get(MemoryBufferRef(TBDv5File, "Test.tbd"));
EXPECT_FALSE(!!Result);
std::string ErrorMessage = toString(Result.takeError());
EXPECT_EQ("invalid exported_symbols section\n", ErrorMessage);
}
TEST(TBDv5, DefaultMinOS) {
static const char TBDv5File[] = R"({
"tapi_tbd_version": 5,
"main_library": {
"target_info": [
{
"target": "arm64-ios-simulator"
}
],
"install_names":[
{ "name":"/S/L/F/Foo.framework/Foo" }
]
}})";
Expected<TBDFile> Result =
TextAPIReader::get(MemoryBufferRef(TBDv5File, "Test.tbd"));
EXPECT_TRUE(!!Result);
TBDFile File = std::move(Result.get());
EXPECT_EQ(FileType::TBD_V5, File->getFileType());
EXPECT_EQ(std::string("/S/L/F/Foo.framework/Foo"), File->getInstallName());
EXPECT_TRUE(File->targets().begin() != File->targets().end());
EXPECT_EQ(*File->targets().begin(),
Target(AK_arm64, PLATFORM_IOSSIMULATOR, VersionTuple(0, 0)));
}
TEST(TBDv5, InvalidMinOS) {
static const char TBDv5File[] = R"({
"tapi_tbd_version": 5,
"main_library": {
"target_info": [
{
"target": "arm64-ios-simulator",
"min_deployment": "swift-abi"
}
],
"install_names":[
{ "name":"/S/L/F/Foo.framework/Foo" }
]
}})";
Expected<TBDFile> Result =
TextAPIReader::get(MemoryBufferRef(TBDv5File, "Test.tbd"));
EXPECT_FALSE(!!Result);
std::string ErrorMessage = toString(Result.takeError());
EXPECT_EQ("invalid min_deployment section\n", ErrorMessage);
}
TEST(TBDv5, SimSupport) {
static const char TBDv5File[] = R"({
"tapi_tbd_version": 5,
"main_library": {
"target_info": [
{
"target": "arm64-macos",
"min_deployment": "11.1"
}
],
"install_names":[
{ "name":"/S/L/F/Foo.framework/Foo" }
],
"flags":[
{ "attributes": ["sim_support"] }
]
}})";
Expected<TBDFile> Result =
TextAPIReader::get(MemoryBufferRef(TBDv5File, "Test.tbd"));
EXPECT_TRUE(!!Result);
Target ExpectedTarget = Target(AK_arm64, PLATFORM_MACOS, VersionTuple(11, 1));
TBDFile ReadFile = std::move(Result.get());
EXPECT_EQ(FileType::TBD_V5, ReadFile->getFileType());
EXPECT_EQ(std::string("/S/L/F/Foo.framework/Foo"),
ReadFile->getInstallName());
EXPECT_TRUE(ReadFile->targets().begin() != ReadFile->targets().end());
EXPECT_EQ(*ReadFile->targets().begin(), ExpectedTarget);
EXPECT_TRUE(ReadFile->hasSimulatorSupport());
}
TEST(TBDv5, NotForSharedCache) {
static const char TBDv5File[] = R"({
"tapi_tbd_version": 5,
"main_library": {
"target_info": [
{
"target": "arm64-macos",
"min_deployment": "11.1"
}
],
"install_names":[
{ "name":"/S/L/F/Foo.framework/Foo" }
],
"flags":[
{ "attributes": ["not_for_dyld_shared_cache"] }
]
}})";
Expected<TBDFile> Result =
TextAPIReader::get(MemoryBufferRef(TBDv5File, "Test.tbd"));
EXPECT_TRUE(!!Result);
Target ExpectedTarget = Target(AK_arm64, PLATFORM_MACOS, VersionTuple(11, 1));
TBDFile ReadFile = std::move(Result.get());
EXPECT_EQ(FileType::TBD_V5, ReadFile->getFileType());
EXPECT_EQ(std::string("/S/L/F/Foo.framework/Foo"),
ReadFile->getInstallName());
EXPECT_TRUE(ReadFile->targets().begin() != ReadFile->targets().end());
EXPECT_EQ(*ReadFile->targets().begin(), ExpectedTarget);
EXPECT_FALSE(ReadFile->hasSimulatorSupport());
EXPECT_TRUE(ReadFile->isOSLibNotForSharedCache());
}
TEST(TBDv5, ObjCInterfaces) {
static const char TBDv5File[] = R"({
"tapi_tbd_version": 5,
"main_library": {
"target_info": [
{
"target": "arm64-ios-simulator",
"min_deployment": "14.0"
}
],
"install_names":[
{ "name":"/S/L/F/Foo.framework/Foo" }
],
"exported_symbols": [
{
"data": {
"global": [
"_global",
"_OBJC_METACLASS_$_Standalone",
"_OBJC_CLASS_$_Standalone2"
],
"weak": ["_OBJC_EHTYPE_$_NSObject"],
"objc_class": [
"ClassA",
"ClassB"
],
"objc_eh_type": ["ClassA"]
}
}]
}})";
Expected<TBDFile> Result =
TextAPIReader::get(MemoryBufferRef(TBDv5File, "Test.tbd"));
EXPECT_TRUE(!!Result);
TBDFile File = std::move(Result.get());
EXPECT_EQ(FileType::TBD_V5, File->getFileType());
Target ExpectedTarget =
Target(AK_arm64, PLATFORM_IOSSIMULATOR, VersionTuple(14, 0));
EXPECT_EQ(*File->targets().begin(), ExpectedTarget);
// Check Symbols.
ExportedSymbolSeq Exports;
for (const auto *Sym : File->symbols()) {
ExportedSymbol Temp =
ExportedSymbol{Sym->getKind(), std::string(Sym->getName()),
Sym->isWeakDefined() || Sym->isWeakReferenced(),
Sym->isThreadLocalValue(), Sym->isData()};
Exports.emplace_back(std::move(Temp));
}
llvm::sort(Exports);
std::vector<ExportedSymbol> ExpectedExports = {
{EncodeKind::GlobalSymbol, "_OBJC_CLASS_$_Standalone2", false, false,
true},
{EncodeKind::GlobalSymbol, "_OBJC_EHTYPE_$_NSObject", true, false, true},
{EncodeKind::GlobalSymbol, "_OBJC_METACLASS_$_Standalone", false, false,
true},
{EncodeKind::GlobalSymbol, "_global", false, false, true},
{EncodeKind::ObjectiveCClass, "ClassA", false, false, true},
{EncodeKind::ObjectiveCClass, "ClassB", false, false, true},
{EncodeKind::ObjectiveCClassEHType, "ClassA", false, false, true}};
EXPECT_EQ(ExpectedExports.size(), Exports.size());
EXPECT_TRUE(
std::equal(Exports.begin(), Exports.end(), std::begin(ExpectedExports)));
SmallString<4096> Buffer;
raw_svector_ostream OS(Buffer);
Error WriteResult = TextAPIWriter::writeToStream(OS, *File);
EXPECT_TRUE(!WriteResult);
Expected<TBDFile> Output =
TextAPIReader::get(MemoryBufferRef(Buffer, "Output.tbd"));
EXPECT_TRUE(!!Output);
TBDFile WriteResultFile = std::move(Output.get());
EXPECT_EQ(*File, *WriteResultFile);
}
TEST(TBDv5, MergeIF) {
static const char TBDv5FileA[] = R"({
"tapi_tbd_version": 5,
"main_library": {
"target_info": [
{
"target": "x86_64-macos",
"min_deployment": "10.14"
},
{
"target": "arm64-macos",
"min_deployment": "10.14"
},
{
"target": "arm64-maccatalyst",
"min_deployment": "12.1"
}
],
"flags": [
{
"targets": [
"x86_64-macos"
],
"attributes": [
"flat_namespace"
]
}
],
"install_names": [
{
"name": "/S/L/F/Foo.framework/Foo"
}
],
"current_versions": [
{
"version": "1.2"
}
],
"compatibility_versions": [
{ "version": "1.1" }
],
"rpaths": [
{
"targets": [
"x86_64-macos"
],
"paths": [
"@executable_path/.../Frameworks"
]
}
],
"parent_umbrellas": [
{
"umbrella": "System"
}
],
"allowable_clients": [
{
"clients": [
"ClientA",
"ClientB"
]
}
],
"reexported_libraries": [
{
"names": [
"/u/l/l/libfoo.dylib",
"/u/l/l/libbar.dylib"
]
}
],
"exported_symbols": [
{
"targets": [
"x86_64-macos",
"arm64-macos"
],
"data": {
"global": [
"_global"
],
"objc_class": [
"ClassA"
],
"weak": [],
"thread_local": []
},
"text": {
"global": [
"_func"
],
"weak": [],
"thread_local": []
}
},
{
"targets": [
"x86_64-macos"
],
"data": {
"global": [
"_globalVar"
],
"objc_class": [
"ClassA",
"ClassB",
"ClassData"
],
"objc_eh_type": [
"ClassA",
"ClassB"
],
"objc_ivar": [
"ClassA.ivar1",
"ClassA.ivar2",
"ClassC.ivar1"
]
},
"text": {
"global": [
"_funcFoo"
]
}
}
],
"reexported_symbols": [
{
"targets": [
"x86_64-macos",
"arm64-macos"
],
"data": {
"global": [
"_globalRe"
],
"objc_class": [
"ClassRexport"
]
},
"text": {
"global": [
"_funcA"
]
}
}
],
"undefined_symbols": [
{
"targets": [
"x86_64-macos"
],
"data": {
"global": [
"_globalBind"
],
"weak": [
"referenced_sym"
]
}
}
]
},
"libraries": []
})";
static const char TBDv5FileB[] = R"({
"tapi_tbd_version": 5,
"main_library": {
"target_info": [
{
"target": "x86_64-macos",
"min_deployment": "10.14"
},
{
"target": "arm64-macos",
"min_deployment": "10.14"
},
{
"target": "arm64-maccatalyst",
"min_deployment": "12.1"
}
],
"flags": [
{
"targets": [
"x86_64-macos"
],
"attributes": [
"flat_namespace"
]
}
],
"install_names": [
{
"name": "/S/L/F/Foo.framework/Foo"
}
],
"current_versions": [
{
"version": "1.2"
}
],
"compatibility_versions": [
{ "version": "1.1" }
],
"exported_symbols": [
{
"targets": [
"x86_64-macos",
"arm64-macos"
],
"data": {
"global": [
"_globalZ"
],
"objc_class": [
"ClassZ"
],
"weak": [],
"thread_local": []
},
"text": {
"global": [
"_funcZ"
],
"weak": [],
"thread_local": []
}
},
{
"targets": [
"x86_64-macos"
],
"data": {
"global": [
"_globalVarZ"
],
"objc_class": [
"ClassZ",
"ClassF"
],
"objc_eh_type": [
"ClassZ",
"ClassF"
],
"objc_ivar": [
"ClassZ.ivar1",
"ClassZ.ivar2",
"ClassF.ivar1"
]
},
"text": {
"global": [
"_funcFooZ"
]
}
}
]
},
"libraries": []
})";
Expected<TBDFile> ResultA =
TextAPIReader::get(MemoryBufferRef(TBDv5FileA, "Test.tbd"));
EXPECT_TRUE(!!ResultA);
TBDFile FileA = std::move(ResultA.get());
Expected<TBDFile> ResultB =
TextAPIReader::get(MemoryBufferRef(TBDv5FileB, "Test.tbd"));
EXPECT_TRUE(!!ResultB);
TBDFile FileB = std::move(ResultB.get());
Expected<TBDFile> MergedResult = FileA->merge(FileB.get());
EXPECT_TRUE(!!MergedResult);
TBDFile MergedFile = std::move(MergedResult.get());
EXPECT_EQ(FileType::TBD_V5, MergedFile->getFileType());
EXPECT_EQ(std::string("/S/L/F/Foo.framework/Foo"),
MergedFile->getInstallName());
TargetList AllTargets = {
Target(AK_x86_64, PLATFORM_MACOS, VersionTuple(10, 14)),
Target(AK_arm64, PLATFORM_MACOS, VersionTuple(11, 0, 0)),
Target(AK_arm64, PLATFORM_MACCATALYST, VersionTuple(14, 0)),
};
EXPECT_EQ(mapToPlatformSet(AllTargets), MergedFile->getPlatforms());
EXPECT_EQ(mapToArchitectureSet(AllTargets), MergedFile->getArchitectures());
EXPECT_EQ(PackedVersion(1, 2, 0), MergedFile->getCurrentVersion());
EXPECT_EQ(PackedVersion(1, 1, 0), MergedFile->getCompatibilityVersion());
EXPECT_TRUE(MergedFile->isApplicationExtensionSafe());
EXPECT_FALSE(MergedFile->isTwoLevelNamespace());
EXPECT_EQ(0U, MergedFile->documents().size());
InterfaceFileRef ClientA("ClientA", AllTargets);
InterfaceFileRef ClientB("ClientB", AllTargets);
EXPECT_EQ(2U, MergedFile->allowableClients().size());
EXPECT_EQ(ClientA, MergedFile->allowableClients().at(0));
EXPECT_EQ(ClientB, MergedFile->allowableClients().at(1));
InterfaceFileRef ReexportA("/u/l/l/libbar.dylib", AllTargets);
InterfaceFileRef ReexportB("/u/l/l/libfoo.dylib", AllTargets);
EXPECT_EQ(2U, MergedFile->reexportedLibraries().size());
EXPECT_EQ(ReexportA, MergedFile->reexportedLibraries().at(0));
EXPECT_EQ(ReexportB, MergedFile->reexportedLibraries().at(1));
TargetToAttr RPaths = {
{Target(AK_x86_64, PLATFORM_MACOS), "@executable_path/.../Frameworks"},
};
EXPECT_EQ(RPaths, MergedFile->rpaths());
TargetToAttr Umbrellas = {{Target(AK_x86_64, PLATFORM_MACOS), "System"},
{Target(AK_arm64, PLATFORM_MACOS), "System"},
{Target(AK_arm64, PLATFORM_MACCATALYST), "System"}};
EXPECT_EQ(Umbrellas, MergedFile->umbrellas());
ExportedSymbolSeq Exports, Reexports, Undefineds;
for (const auto *Sym : MergedFile->symbols()) {
TargetList SymTargets{Sym->targets().begin(), Sym->targets().end()};
ExportedSymbol Temp =
ExportedSymbol{Sym->getKind(),
std::string(Sym->getName()),
Sym->isWeakDefined() || Sym->isWeakReferenced(),
Sym->isThreadLocalValue(),
Sym->isData(),
SymTargets};
if (Sym->isUndefined())
Undefineds.emplace_back(std::move(Temp));
else
Sym->isReexported() ? Reexports.emplace_back(std::move(Temp))
: Exports.emplace_back(std::move(Temp));
}
llvm::sort(Exports);
llvm::sort(Reexports);
llvm::sort(Undefineds);
TargetList MacOSTargets = {Target(AK_x86_64, PLATFORM_MACOS),
Target(AK_arm64, PLATFORM_MACOS)};
std::vector<ExportedSymbol> ExpectedExportedSymbols = {
{EncodeKind::GlobalSymbol, "_func", false, false, false, MacOSTargets},
{EncodeKind::GlobalSymbol,
"_funcFoo",
false,
false,
false,
{Target(AK_x86_64, PLATFORM_MACOS)}},
{EncodeKind::GlobalSymbol,
"_funcFooZ",
false,
false,
false,
{Target(AK_x86_64, PLATFORM_MACOS)}},
{EncodeKind::GlobalSymbol, "_funcZ", false, false, false, MacOSTargets},
{EncodeKind::GlobalSymbol, "_global", false, false, true, MacOSTargets},
{EncodeKind::GlobalSymbol,
"_globalVar",
false,
false,
true,
{Target(AK_x86_64, PLATFORM_MACOS)}},
{EncodeKind::GlobalSymbol,
"_globalVarZ",
false,
false,
true,
{Target(AK_x86_64, PLATFORM_MACOS)}},
{EncodeKind::GlobalSymbol, "_globalZ", false, false, true, MacOSTargets},
{EncodeKind::ObjectiveCClass,
"ClassA",
false,
false,
true,
{Target(AK_x86_64, PLATFORM_MACOS)}},
{EncodeKind::ObjectiveCClass,
"ClassB",
false,
false,
true,
{Target(AK_x86_64, PLATFORM_MACOS)}},
{EncodeKind::ObjectiveCClass,
"ClassData",
false,
false,
true,
{Target(AK_x86_64, PLATFORM_MACOS)}},
{EncodeKind::ObjectiveCClass,
"ClassF",
false,
false,
true,
{Target(AK_x86_64, PLATFORM_MACOS)}},
{EncodeKind::ObjectiveCClass,
"ClassZ",
false,
false,
true,
{Target(AK_x86_64, PLATFORM_MACOS)}},
{EncodeKind::ObjectiveCClassEHType,
"ClassA",
false,
false,
true,
{Target(AK_x86_64, PLATFORM_MACOS)}},
{EncodeKind::ObjectiveCClassEHType,
"ClassB",
false,
false,
true,
{Target(AK_x86_64, PLATFORM_MACOS)}},
{EncodeKind::ObjectiveCClassEHType,
"ClassF",
false,
false,
true,
{Target(AK_x86_64, PLATFORM_MACOS)}},
{EncodeKind::ObjectiveCClassEHType,
"ClassZ",
false,
false,
true,
{Target(AK_x86_64, PLATFORM_MACOS)}},
{EncodeKind::ObjectiveCInstanceVariable,
"ClassA.ivar1",
false,
false,
true,
{Target(AK_x86_64, PLATFORM_MACOS)}},
{EncodeKind::ObjectiveCInstanceVariable,
"ClassA.ivar2",
false,
false,
true,
{Target(AK_x86_64, PLATFORM_MACOS)}},
{EncodeKind::ObjectiveCInstanceVariable,
"ClassC.ivar1",
false,
false,
true,
{Target(AK_x86_64, PLATFORM_MACOS)}},
{EncodeKind::ObjectiveCInstanceVariable,
"ClassF.ivar1",
false,
false,
true,
{Target(AK_x86_64, PLATFORM_MACOS)}},
{EncodeKind::ObjectiveCInstanceVariable,
"ClassZ.ivar1",
false,
false,
true,
{Target(AK_x86_64, PLATFORM_MACOS)}},
{EncodeKind::ObjectiveCInstanceVariable,
"ClassZ.ivar2",
false,
false,
true,
{Target(AK_x86_64, PLATFORM_MACOS)}},
};
std::vector<ExportedSymbol> ExpectedReexportedSymbols = {
{EncodeKind::GlobalSymbol, "_funcA", false, false, false, MacOSTargets},
{EncodeKind::GlobalSymbol, "_globalRe", false, false, true, MacOSTargets},
{EncodeKind::ObjectiveCClass, "ClassRexport", false, false, true,
MacOSTargets},
};
std::vector<ExportedSymbol> ExpectedUndefinedSymbols = {
{EncodeKind::GlobalSymbol,
"_globalBind",
false,
false,
true,
{Target(AK_x86_64, PLATFORM_MACOS)}},
{EncodeKind::GlobalSymbol,
"referenced_sym",
true,
false,
true,
{Target(AK_x86_64, PLATFORM_MACOS)}},
};
EXPECT_EQ(ExpectedExportedSymbols.size(), Exports.size());
EXPECT_EQ(ExpectedReexportedSymbols.size(), Reexports.size());
EXPECT_EQ(ExpectedUndefinedSymbols.size(), Undefineds.size());
EXPECT_TRUE(std::equal(Exports.begin(), Exports.end(),
std::begin(ExpectedExportedSymbols)));
EXPECT_TRUE(std::equal(Reexports.begin(), Reexports.end(),
std::begin(ExpectedReexportedSymbols)));
EXPECT_TRUE(std::equal(Undefineds.begin(), Undefineds.end(),
std::begin(ExpectedUndefinedSymbols)));
}
TEST(TBDv5, ExtractIF) {
static const char TBDv5File[] = R"({
"tapi_tbd_version": 5,
"main_library": {
"target_info": [
{
"target": "x86_64-macos",
"min_deployment": "10.14"
},
{
"target": "arm64-macos",
"min_deployment": "10.14"
},
{
"target": "arm64-maccatalyst",
"min_deployment": "12.1"
}
],
"flags": [
{
"targets": [
"x86_64-macos"
],
"attributes": [
"flat_namespace"
]
}
],
"install_names": [
{
"name": "/S/L/F/Foo.framework/Foo"
}
],
"current_versions": [
{
"version": "1.2"
}
],
"compatibility_versions": [
{ "version": "1.1" }
],
"rpaths": [
{
"targets": [
"x86_64-macos"
],
"paths": [
"@executable_path/.../Frameworks"
]
}
],
"parent_umbrellas": [
{
"umbrella": "System"
}
],
"allowable_clients": [
{
"clients": [
"ClientA",
"ClientB"
]
}
],
"reexported_libraries": [
{
"names": [
"/u/l/l/libfoo.dylib",
"/u/l/l/libbar.dylib"
]
}
],
"exported_symbols": [
{
"targets": [
"x86_64-macos",
"arm64-macos"
],
"data": {
"global": [
"_global"
],
"objc_class": [
"ClassA"
],
"weak": [],
"thread_local": []
},
"text": {
"global": [
"_func"
],
"weak": [],
"thread_local": []
}
},
{
"targets": [
"x86_64-macos"
],
"data": {
"global": [
"_globalVar"
],
"objc_class": [
"ClassA",
"ClassB",
"ClassData"
],
"objc_eh_type": [
"ClassA",
"ClassB"
],
"objc_ivar": [
"ClassA.ivar1",
"ClassA.ivar2",
"ClassC.ivar1"
]
},
"text": {
"global": [
"_funcFoo"
]
}
}
],
"reexported_symbols": [
{
"targets": [
"x86_64-macos",
"arm64-macos"
],
"data": {
"global": [
"_globalRe"
],
"objc_class": [
"ClassRexport"
]
},
"text": {
"global": [
"_funcA"
]
}
}
],
"undefined_symbols": [
{
"targets": [
"x86_64-macos"
],
"data": {
"global": [
"_globalBind"
],
"weak": [
"referenced_sym"
]
}
}
]
},
"libraries": []
})";
Expected<TBDFile> Result =
TextAPIReader::get(MemoryBufferRef(TBDv5File, "Test.tbd"));
EXPECT_TRUE(!!Result);
TBDFile File = std::move(Result.get());
Expected<TBDFile> ExtractedResult = File->extract(AK_arm64);
EXPECT_TRUE(!!ExtractedResult);
TBDFile ExtractedFile = std::move(ExtractedResult.get());
EXPECT_EQ(FileType::TBD_V5, ExtractedFile->getFileType());
EXPECT_EQ(std::string("/S/L/F/Foo.framework/Foo"),
ExtractedFile->getInstallName());
TargetList AllTargets = {
Target(AK_arm64, PLATFORM_MACOS, VersionTuple(11, 0, 0)),
Target(AK_arm64, PLATFORM_MACCATALYST, VersionTuple(14, 0)),
};
EXPECT_EQ(mapToPlatformSet(AllTargets), ExtractedFile->getPlatforms());
EXPECT_EQ(mapToArchitectureSet(AllTargets),
ExtractedFile->getArchitectures());
EXPECT_EQ(PackedVersion(1, 2, 0), ExtractedFile->getCurrentVersion());
EXPECT_EQ(PackedVersion(1, 1, 0), ExtractedFile->getCompatibilityVersion());
EXPECT_TRUE(ExtractedFile->isApplicationExtensionSafe());
EXPECT_FALSE(ExtractedFile->isTwoLevelNamespace());
EXPECT_EQ(0U, ExtractedFile->documents().size());
InterfaceFileRef ClientA("ClientA", AllTargets);
InterfaceFileRef ClientB("ClientB", AllTargets);
EXPECT_EQ(2U, ExtractedFile->allowableClients().size());
EXPECT_EQ(ClientA, ExtractedFile->allowableClients().at(0));
EXPECT_EQ(ClientB, ExtractedFile->allowableClients().at(1));
InterfaceFileRef ReexportA("/u/l/l/libbar.dylib", AllTargets);
InterfaceFileRef ReexportB("/u/l/l/libfoo.dylib", AllTargets);
EXPECT_EQ(2U, ExtractedFile->reexportedLibraries().size());
EXPECT_EQ(ReexportA, ExtractedFile->reexportedLibraries().at(0));
EXPECT_EQ(ReexportB, ExtractedFile->reexportedLibraries().at(1));
EXPECT_EQ(0u, ExtractedFile->rpaths().size());
TargetToAttr Umbrellas = {{Target(AK_arm64, PLATFORM_MACOS), "System"},
{Target(AK_arm64, PLATFORM_MACCATALYST), "System"}};
EXPECT_EQ(Umbrellas, ExtractedFile->umbrellas());
ExportedSymbolSeq Exports, Reexports, Undefineds;
for (const auto *Sym : ExtractedFile->symbols()) {
TargetList SymTargets{Sym->targets().begin(), Sym->targets().end()};
ExportedSymbol Temp =
ExportedSymbol{Sym->getKind(),
std::string(Sym->getName()),
Sym->isWeakDefined() || Sym->isWeakReferenced(),
Sym->isThreadLocalValue(),
Sym->isData(),
SymTargets};
if (Sym->isUndefined())
Undefineds.emplace_back(std::move(Temp));
else
Sym->isReexported() ? Reexports.emplace_back(std::move(Temp))
: Exports.emplace_back(std::move(Temp));
}
llvm::sort(Exports);
llvm::sort(Reexports);
llvm::sort(Undefineds);
TargetList MacOSTargets = {Target(AK_arm64, PLATFORM_MACOS)};
std::vector<ExportedSymbol> ExpectedExportedSymbols = {
{EncodeKind::GlobalSymbol, "_func", false, false, false, MacOSTargets},
{EncodeKind::GlobalSymbol, "_global", false, false, true, MacOSTargets},
{EncodeKind::ObjectiveCClass, "ClassA", false, false, true, MacOSTargets},
};
std::vector<ExportedSymbol> ExpectedReexportedSymbols = {
{EncodeKind::GlobalSymbol, "_funcA", false, false, false, MacOSTargets},
{EncodeKind::GlobalSymbol, "_globalRe", false, false, true, MacOSTargets},
{EncodeKind::ObjectiveCClass, "ClassRexport", false, false, true,
MacOSTargets},
};
EXPECT_EQ(ExpectedExportedSymbols.size(), Exports.size());
EXPECT_EQ(ExpectedReexportedSymbols.size(), Reexports.size());
EXPECT_EQ(0U, Undefineds.size());
EXPECT_TRUE(std::equal(Exports.begin(), Exports.end(),
std::begin(ExpectedExportedSymbols)));
EXPECT_TRUE(std::equal(Reexports.begin(), Reexports.end(),
std::begin(ExpectedReexportedSymbols)));
}
TEST(TBDv5, RemoveIF) {
static const char TBDv5File[] = R"({
"tapi_tbd_version": 5,
"main_library": {
"target_info": [
{
"target": "x86_64-macos",
"min_deployment": "10.14"
},
{
"target": "arm64-macos",
"min_deployment": "10.14"
},
{
"target": "arm64-maccatalyst",
"min_deployment": "12.1"
}
],
"flags": [
{
"targets": [
"x86_64-macos"
],
"attributes": [
"flat_namespace",
"not_for_dyld_shared_cache"
]
}
],
"install_names": [
{
"name": "/S/L/F/Foo.framework/Foo"
}
],
"current_versions": [
{
"version": "1.2"
}
],
"compatibility_versions": [
{ "version": "1.1" }
],
"rpaths": [
{
"targets": [
"x86_64-macos"
],
"paths": [
"@executable_path/.../Frameworks"
]
}
],
"parent_umbrellas": [
{
"umbrella": "System"
}
],
"allowable_clients": [
{
"clients": [
"ClientA",
"ClientB"
]
}
],
"reexported_libraries": [
{
"names": [
"/u/l/l/libfoo.dylib",
"/u/l/l/libbar.dylib"
]
}
],
"exported_symbols": [
{
"targets": [
"x86_64-macos",
"arm64-macos"
],
"data": {
"global": [
"_global"
],
"objc_class": [
"ClassA"
],
"weak": [],
"thread_local": []
},
"text": {
"global": [
"_func"
],
"weak": [],
"thread_local": []
}
},
{
"targets": [
"x86_64-macos"
],
"data": {
"global": [
"_globalVar"
],
"objc_class": [
"ClassA",
"ClassB",
"ClassData"
],
"objc_eh_type": [
"ClassA",
"ClassB"
],
"objc_ivar": [
"ClassA.ivar1",
"ClassA.ivar2",
"ClassC.ivar1"
]
},
"text": {
"global": [
"_funcFoo"
]
}
}
],
"reexported_symbols": [
{
"targets": [
"x86_64-macos",
"arm64-macos"
],
"data": {
"global": [
"_globalRe"
],
"objc_class": [
"ClassRexport"
]
},
"text": {
"global": [
"_funcA"
]
}
}
],
"undefined_symbols": [
{
"targets": [
"x86_64-macos"
],
"data": {
"global": [
"_globalBind"
],
"weak": [
"referenced_sym"
]
}
}
]
},
"libraries": []
})";
Expected<TBDFile> Result =
TextAPIReader::get(MemoryBufferRef(TBDv5File, "Test.tbd"));
EXPECT_TRUE(!!Result);
TBDFile File = std::move(Result.get());
Expected<TBDFile> RemovedResult = File->remove(AK_x86_64);
EXPECT_TRUE(!!RemovedResult);
TBDFile RemovedFile = std::move(RemovedResult.get());
EXPECT_EQ(FileType::TBD_V5, RemovedFile->getFileType());
EXPECT_EQ(std::string("/S/L/F/Foo.framework/Foo"),
RemovedFile->getInstallName());
TargetList AllTargets = {
Target(AK_arm64, PLATFORM_MACOS, VersionTuple(11, 0, 0)),
Target(AK_arm64, PLATFORM_MACCATALYST, VersionTuple(14, 0)),
};
EXPECT_EQ(mapToPlatformSet(AllTargets), RemovedFile->getPlatforms());
EXPECT_EQ(mapToArchitectureSet(AllTargets), RemovedFile->getArchitectures());
EXPECT_EQ(PackedVersion(1, 2, 0), RemovedFile->getCurrentVersion());
EXPECT_EQ(PackedVersion(1, 1, 0), RemovedFile->getCompatibilityVersion());
EXPECT_TRUE(RemovedFile->isApplicationExtensionSafe());
EXPECT_TRUE(RemovedFile->isOSLibNotForSharedCache());
EXPECT_FALSE(RemovedFile->isTwoLevelNamespace());
EXPECT_EQ(0U, RemovedFile->documents().size());
InterfaceFileRef ClientA("ClientA", AllTargets);
InterfaceFileRef ClientB("ClientB", AllTargets);
EXPECT_EQ(2U, RemovedFile->allowableClients().size());
EXPECT_EQ(ClientA, RemovedFile->allowableClients().at(0));
EXPECT_EQ(ClientB, RemovedFile->allowableClients().at(1));
InterfaceFileRef ReexportA("/u/l/l/libbar.dylib", AllTargets);
InterfaceFileRef ReexportB("/u/l/l/libfoo.dylib", AllTargets);
EXPECT_EQ(2U, RemovedFile->reexportedLibraries().size());
EXPECT_EQ(ReexportA, RemovedFile->reexportedLibraries().at(0));
EXPECT_EQ(ReexportB, RemovedFile->reexportedLibraries().at(1));
EXPECT_EQ(0u, RemovedFile->rpaths().size());
TargetToAttr Umbrellas = {{Target(AK_arm64, PLATFORM_MACOS), "System"},
{Target(AK_arm64, PLATFORM_MACCATALYST), "System"}};
EXPECT_EQ(Umbrellas, RemovedFile->umbrellas());
ExportedSymbolSeq Exports, Reexports, Undefineds;
for (const auto *Sym : RemovedFile->symbols()) {
TargetList SymTargets{Sym->targets().begin(), Sym->targets().end()};
ExportedSymbol Temp =
ExportedSymbol{Sym->getKind(),
std::string(Sym->getName()),
Sym->isWeakDefined() || Sym->isWeakReferenced(),
Sym->isThreadLocalValue(),
Sym->isData(),
SymTargets};
if (Sym->isUndefined())
Undefineds.emplace_back(std::move(Temp));
else
Sym->isReexported() ? Reexports.emplace_back(std::move(Temp))
: Exports.emplace_back(std::move(Temp));
}
llvm::sort(Exports);
llvm::sort(Reexports);
llvm::sort(Undefineds);
TargetList MacOSTargets = {Target(AK_arm64, PLATFORM_MACOS)};
std::vector<ExportedSymbol> ExpectedExportedSymbols = {
{EncodeKind::GlobalSymbol, "_func", false, false, false, MacOSTargets},
{EncodeKind::GlobalSymbol, "_global", false, false, true, MacOSTargets},
{EncodeKind::ObjectiveCClass, "ClassA", false, false, true, MacOSTargets},
};
std::vector<ExportedSymbol> ExpectedReexportedSymbols = {
{EncodeKind::GlobalSymbol, "_funcA", false, false, false, MacOSTargets},
{EncodeKind::GlobalSymbol, "_globalRe", false, false, true, MacOSTargets},
{EncodeKind::ObjectiveCClass, "ClassRexport", false, false, true,
MacOSTargets},
};
EXPECT_EQ(ExpectedExportedSymbols.size(), Exports.size());
EXPECT_EQ(ExpectedReexportedSymbols.size(), Reexports.size());
EXPECT_EQ(0U, Undefineds.size());
EXPECT_TRUE(std::equal(Exports.begin(), Exports.end(),
std::begin(ExpectedExportedSymbols)));
EXPECT_TRUE(std::equal(Reexports.begin(), Reexports.end(),
std::begin(ExpectedReexportedSymbols)));
}
TEST(TBDv5, InlineIF) {
static const char UmbrellaFile[] = R"({
"tapi_tbd_version": 5,
"main_library": {
"target_info": [
{
"target": "x86_64-macos",
"min_deployment": "10.14"
},
{
"target": "arm64-macos",
"min_deployment": "10.14"
}
],
"install_names": [
{
"name": "/S/L/F/Foo.framework/Foo"
}
],
"current_versions": [
{
"version": "1.2"
}
],
"reexported_libraries": [
{
"names": [
"/u/l/l/libfoo.dylib",
"/u/l/l/libbar.dylib"
]
}
]
}})";
static const char ReexportFile[] = R"({
"tapi_tbd_version": 5,
"main_library": {
"target_info": [
{
"target": "x86_64-macos",
"min_deployment": "10.14"
},
{
"target": "arm64-macos",
"min_deployment": "10.14"
}
],
"install_names": [
{
"name" : "/u/l/l/libfoo.dylib"
}
],
"current_versions": [
{
"version": "1"
}
],
"rpaths": [
{
"targets": [
"x86_64-macos"
],
"paths": [
"@executable_path/.../Frameworks"
]
}
],
"exported_symbols": [
{
"targets": [
"x86_64-macos",
"arm64-macos"
],
"data": {
"global": [
"_global"
],
"objc_class": [
"ClassA"
],
"weak": [],
"thread_local": []
}
}
]}})";
Expected<TBDFile> UmbrellaResult =
TextAPIReader::get(MemoryBufferRef(UmbrellaFile, "Test.tbd"));
EXPECT_TRUE(!!UmbrellaResult);
TBDFile Umbrella = std::move(UmbrellaResult.get());
Expected<TBDFile> ReexportResult =
TextAPIReader::get(MemoryBufferRef(ReexportFile, "Test.tbd"));
EXPECT_TRUE(!!ReexportResult);
TBDReexportFile Reexport = std::move(ReexportResult.get());
Umbrella->inlineLibrary(Reexport);
EXPECT_EQ(FileType::TBD_V5, Umbrella->getFileType());
EXPECT_EQ(std::string("/S/L/F/Foo.framework/Foo"),
Umbrella->getInstallName());
TargetList AllTargets = {
Target(AK_x86_64, PLATFORM_MACOS, VersionTuple(10, 14)),
Target(AK_arm64, PLATFORM_MACOS, VersionTuple(11, 0, 0)),
};
EXPECT_EQ(mapToPlatformSet(AllTargets), Umbrella->getPlatforms());
EXPECT_EQ(mapToArchitectureSet(AllTargets), Umbrella->getArchitectures());
EXPECT_EQ(PackedVersion(1, 2, 0), Umbrella->getCurrentVersion());
EXPECT_EQ(PackedVersion(1, 0, 0), Umbrella->getCompatibilityVersion());
InterfaceFileRef ReexportA("/u/l/l/libbar.dylib", AllTargets);
InterfaceFileRef ReexportB("/u/l/l/libfoo.dylib", AllTargets);
EXPECT_EQ(2U, Umbrella->reexportedLibraries().size());
EXPECT_EQ(ReexportA, Umbrella->reexportedLibraries().at(0));
EXPECT_EQ(ReexportB, Umbrella->reexportedLibraries().at(1));
EXPECT_EQ(1U, Umbrella->documents().size());
TBDReexportFile Document = Umbrella->documents().front();
EXPECT_EQ(std::string("/u/l/l/libfoo.dylib"), Document->getInstallName());
EXPECT_EQ(0U, Document->getSwiftABIVersion());
EXPECT_TRUE(Document->isTwoLevelNamespace());
EXPECT_TRUE(Document->isApplicationExtensionSafe());
EXPECT_EQ(PackedVersion(1, 0, 0), Document->getCurrentVersion());
EXPECT_EQ(PackedVersion(1, 0, 0), Document->getCompatibilityVersion());
ExportedSymbolSeq Exports;
for (const auto *Sym : Document->symbols()) {
TargetList SymTargets{Sym->targets().begin(), Sym->targets().end()};
Exports.emplace_back(
ExportedSymbol{Sym->getKind(), std::string(Sym->getName()),
Sym->isWeakDefined() || Sym->isWeakReferenced(),
Sym->isThreadLocalValue(), Sym->isData(), SymTargets});
}
llvm::sort(Exports);
ExportedSymbolSeq ExpectedExports = {
{EncodeKind::GlobalSymbol, "_global", false, false, true, AllTargets},
{EncodeKind::ObjectiveCClass, "ClassA", false, false, true, AllTargets},
};
EXPECT_EQ(ExpectedExports.size(), Exports.size());
EXPECT_TRUE(
std::equal(Exports.begin(), Exports.end(), std::begin(ExpectedExports)));
}
} // end namespace TBDv5