diff options
author | Cyndy Ishida <cyndy_ishida@apple.com> | 2023-02-22 10:00:07 -0800 |
---|---|---|
committer | Cyndy Ishida <cyndy_ishida@apple.com> | 2023-02-22 11:34:27 -0800 |
commit | 8217932aabcb271df7eb30e069fdace904299cba (patch) | |
tree | ff6b154de5720fad8555d5816ad080ff2e0e032b /llvm/unittests/TextAPI | |
parent | 34a8e6eee666fe1e67b8b689abe5e4e95bf6b166 (diff) | |
download | llvm-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.cpp | 520 |
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 |