aboutsummaryrefslogtreecommitdiff
path: root/llvm/unittests/TextAPI
diff options
context:
space:
mode:
authorCyndy Ishida <cyndy_ishida@apple.com>2023-02-22 10:00:07 -0800
committerCyndy Ishida <cyndy_ishida@apple.com>2023-02-22 11:34:27 -0800
commit8217932aabcb271df7eb30e069fdace904299cba (patch)
treeff6b154de5720fad8555d5816ad080ff2e0e032b /llvm/unittests/TextAPI
parent34a8e6eee666fe1e67b8b689abe5e4e95bf6b166 (diff)
downloadllvm-8217932aabcb271df7eb30e069fdace904299cba.zip
llvm-8217932aabcb271df7eb30e069fdace904299cba.tar.gz
llvm-8217932aabcb271df7eb30e069fdace904299cba.tar.bz2
[TextAPI] Implement TBDv5 Writer
Create writer for new JSON format. The new JSON format allows practically all attributes to be defined per target in a universal library however the internal representation only allows one for the time being. For now the write will always write those attributes as default available for all targets (install name, compatability & current version, swift abi, flags e.g. flatnamepace & app exenstion safety) rdar://102076911 Reviewed By: ributzka Differential Revision: https://reviews.llvm.org/D144339
Diffstat (limited to 'llvm/unittests/TextAPI')
-rw-r--r--llvm/unittests/TextAPI/TextStubV5Tests.cpp520
1 files changed, 520 insertions, 0 deletions
diff --git a/llvm/unittests/TextAPI/TextStubV5Tests.cpp b/llvm/unittests/TextAPI/TextStubV5Tests.cpp
index c9668bb..2536a5a 100644
--- a/llvm/unittests/TextAPI/TextStubV5Tests.cpp
+++ b/llvm/unittests/TextAPI/TextStubV5Tests.cpp
@@ -520,4 +520,524 @@ TEST(TBDv5, ReadMultipleDocuments) {
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": [
+ "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(AllTargets[0], "@executable_path/.../Frameworks");
+
+ 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(SymbolKind::GlobalSymbol, "_global",
+ {AllTargets[0], AllTargets[1]}, Flags | SymbolFlags::Data);
+ File.addSymbol(SymbolKind::GlobalSymbol, "_func",
+ {AllTargets[0], AllTargets[1]}, Flags | SymbolFlags::Text);
+ File.addSymbol(SymbolKind::ObjectiveCClass, "ClassA",
+ {AllTargets[0], AllTargets[1]}, Flags | SymbolFlags::Data);
+ File.addSymbol(SymbolKind::GlobalSymbol, "_funcFoo", {AllTargets[0]},
+ Flags | SymbolFlags::Text);
+ File.addSymbol(SymbolKind::GlobalSymbol, "_globalVar", {AllTargets[0]},
+ Flags | SymbolFlags::Data);
+ File.addSymbol(SymbolKind::ObjectiveCClass, "ClassData", {AllTargets[0]},
+ Flags | SymbolFlags::Data);
+ File.addSymbol(SymbolKind::ObjectiveCClassEHType, "ClassA", {AllTargets[0]},
+ Flags | SymbolFlags::Data);
+ File.addSymbol(SymbolKind::ObjectiveCClassEHType, "ClassB", {AllTargets[0]},
+ Flags | SymbolFlags::Data);
+ File.addSymbol(SymbolKind::ObjectiveCInstanceVariable, "ClassA.ivar1",
+ {AllTargets[0]}, Flags | SymbolFlags::Data);
+ File.addSymbol(SymbolKind::ObjectiveCInstanceVariable, "ClassA.ivar2",
+ {AllTargets[0]}, Flags | SymbolFlags::Data);
+ File.addSymbol(SymbolKind::ObjectiveCInstanceVariable, "ClassC.ivar1",
+ {AllTargets[0]}, Flags | SymbolFlags::Data);
+
+ // Reexports.
+ Flags = SymbolFlags::Rexported;
+ File.addSymbol(SymbolKind::GlobalSymbol, "_globalRe", AllTargets,
+ Flags | SymbolFlags::Data);
+ File.addSymbol(SymbolKind::GlobalSymbol, "_funcA", AllTargets,
+ Flags | SymbolFlags::Text);
+ File.addSymbol(SymbolKind::ObjectiveCClass, "ClassRexport", AllTargets,
+ Flags | SymbolFlags::Data);
+
+ // Undefineds.
+ Flags = SymbolFlags::Undefined;
+ File.addSymbol(SymbolKind::GlobalSymbol, "_globalBind", {AllTargets[0]},
+ Flags | SymbolFlags::Data);
+ File.addSymbol(SymbolKind::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(AllTargets[0], "@executable_path/.../Frameworks");
+ for (const auto &Targ : AllTargets)
+ NestedFile.addReexportedLibrary("@rpath/libfoo.dylib", Targ);
+ NestedFile.addSymbol(SymbolKind::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(SymbolKind::GlobalSymbol, "_varFooBaz", AllTargets,
+ 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, /*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(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);
+}
+
} // end namespace TBDv5