1 //===-- TextStubV5Tests.cpp - TBD V5 File Test ----------------------------===// 2 // 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 6 // 7 //===-----------------------------------------------------------------------===/ 8 9 #include "TextStubHelpers.h" 10 #include "llvm/TextAPI/InterfaceFile.h" 11 #include "llvm/TextAPI/TextAPIReader.h" 12 #include "llvm/TextAPI/TextAPIWriter.h" 13 #include "gtest/gtest.h" 14 #include <string> 15 #include <vector> 16 17 using namespace llvm; 18 using namespace llvm::MachO; 19 20 namespace TBDv5 { 21 22 TEST(TBDv5, ReadFile) { 23 static const char TBDv5File[] = R"({ 24 "tapi_tbd_version": 5, 25 "main_library": { 26 "target_info": [ 27 { 28 "target": "x86_64-macos", 29 "min_deployment": "10.14" 30 }, 31 { 32 "target": "arm64-macos", 33 "min_deployment": "10.14" 34 }, 35 { 36 "target": "arm64-maccatalyst", 37 "min_deployment": "12.1" 38 } 39 ], 40 "flags": [ 41 { 42 "targets": [ 43 "x86_64-macos" 44 ], 45 "attributes": [ 46 "flat_namespace" 47 ] 48 } 49 ], 50 "install_names": [ 51 { 52 "name": "/S/L/F/Foo.framework/Foo" 53 } 54 ], 55 "current_versions": [ 56 { 57 "version": "1.2" 58 } 59 ], 60 "compatibility_versions": [ 61 { "version": "1.1" } 62 ], 63 "rpaths": [ 64 { 65 "targets": [ 66 "x86_64-macos" 67 ], 68 "paths": [ 69 "@executable_path/.../Frameworks" 70 ] 71 } 72 ], 73 "parent_umbrellas": [ 74 { 75 "umbrella": "System" 76 } 77 ], 78 "allowable_clients": [ 79 { 80 "clients": [ 81 "ClientA", 82 "ClientB" 83 ] 84 } 85 ], 86 "reexported_libraries": [ 87 { 88 "names": [ 89 "/u/l/l/libfoo.dylib", 90 "/u/l/l/libbar.dylib" 91 ] 92 } 93 ], 94 "exported_symbols": [ 95 { 96 "targets": [ 97 "x86_64-macos", 98 "arm64-macos" 99 ], 100 "data": { 101 "global": [ 102 "_global" 103 ], 104 "objc_class": [ 105 "ClassA" 106 ], 107 "weak": [], 108 "thread_local": [] 109 }, 110 "text": { 111 "global": [ 112 "_func" 113 ], 114 "weak": [], 115 "thread_local": [] 116 } 117 }, 118 { 119 "targets": [ 120 "x86_64-macos" 121 ], 122 "data": { 123 "global": [ 124 "_globalVar" 125 ], 126 "objc_class": [ 127 "ClassData" 128 ], 129 "objc_eh_type": [ 130 "ClassA", 131 "ClassB" 132 ], 133 "objc_ivar": [ 134 "ClassA.ivar1", 135 "ClassA.ivar2", 136 "ClassC.ivar1" 137 ] 138 }, 139 "text": { 140 "global": [ 141 "_funcFoo" 142 ] 143 } 144 } 145 ], 146 "reexported_symbols": [ 147 { 148 "targets": [ 149 "x86_64-macos", 150 "arm64-macos" 151 ], 152 "data": { 153 "global": [ 154 "_globalRe" 155 ], 156 "objc_class": [ 157 "ClassRexport" 158 ] 159 }, 160 "text": { 161 "global": [ 162 "_funcA" 163 ] 164 } 165 } 166 ], 167 "undefined_symbols": [ 168 { 169 "targets": [ 170 "x86_64-macos" 171 ], 172 "data": { 173 "global": [ 174 "_globalBind" 175 ], 176 "weak": [ 177 "referenced_sym" 178 ] 179 } 180 } 181 ] 182 }, 183 "libraries": [] 184 })"; 185 186 Expected<TBDFile> Result = 187 TextAPIReader::get(MemoryBufferRef(TBDv5File, "Test.tbd")); 188 EXPECT_TRUE(!!Result); 189 TBDFile File = std::move(Result.get()); 190 EXPECT_EQ(FileType::TBD_V5, File->getFileType()); 191 EXPECT_EQ(std::string("/S/L/F/Foo.framework/Foo"), File->getInstallName()); 192 193 TargetList AllTargets = { 194 Target(AK_x86_64, PLATFORM_MACOS, VersionTuple(10, 14)), 195 Target(AK_arm64, PLATFORM_MACOS, VersionTuple(10, 14)), 196 Target(AK_arm64, PLATFORM_MACCATALYST, VersionTuple(12, 1)), 197 }; 198 EXPECT_EQ(mapToPlatformSet(AllTargets), File->getPlatforms()); 199 EXPECT_EQ(mapToArchitectureSet(AllTargets), File->getArchitectures()); 200 201 EXPECT_EQ(PackedVersion(1, 2, 0), File->getCurrentVersion()); 202 EXPECT_EQ(PackedVersion(1, 1, 0), File->getCompatibilityVersion()); 203 EXPECT_TRUE(File->isApplicationExtensionSafe()); 204 EXPECT_FALSE(File->isTwoLevelNamespace()); 205 EXPECT_EQ(0U, File->documents().size()); 206 207 InterfaceFileRef ClientA("ClientA", AllTargets); 208 InterfaceFileRef ClientB("ClientB", AllTargets); 209 EXPECT_EQ(2U, File->allowableClients().size()); 210 EXPECT_EQ(ClientA, File->allowableClients().at(0)); 211 EXPECT_EQ(ClientB, File->allowableClients().at(1)); 212 213 InterfaceFileRef ReexportA("/u/l/l/libbar.dylib", AllTargets); 214 InterfaceFileRef ReexportB("/u/l/l/libfoo.dylib", AllTargets); 215 EXPECT_EQ(2U, File->reexportedLibraries().size()); 216 EXPECT_EQ(ReexportA, File->reexportedLibraries().at(0)); 217 EXPECT_EQ(ReexportB, File->reexportedLibraries().at(1)); 218 219 TargetToAttr RPaths = { 220 {Target(AK_x86_64, PLATFORM_MACOS), "@executable_path/.../Frameworks"}, 221 }; 222 EXPECT_EQ(RPaths, File->rpaths()); 223 224 TargetToAttr Umbrellas = {{Target(AK_x86_64, PLATFORM_MACOS), "System"}, 225 {Target(AK_arm64, PLATFORM_MACOS), "System"}, 226 {Target(AK_arm64, PLATFORM_MACCATALYST), "System"}}; 227 EXPECT_EQ(Umbrellas, File->umbrellas()); 228 229 ExportedSymbolSeq Exports, Reexports, Undefineds; 230 for (const auto *Sym : File->symbols()) { 231 TargetList SymTargets{Sym->targets().begin(), Sym->targets().end()}; 232 ExportedSymbol Temp = 233 ExportedSymbol{Sym->getKind(), 234 std::string(Sym->getName()), 235 Sym->isWeakDefined() || Sym->isWeakReferenced(), 236 Sym->isThreadLocalValue(), 237 Sym->isData(), 238 SymTargets}; 239 if (Sym->isUndefined()) 240 Undefineds.emplace_back(std::move(Temp)); 241 else 242 Sym->isReexported() ? Reexports.emplace_back(std::move(Temp)) 243 : Exports.emplace_back(std::move(Temp)); 244 } 245 llvm::sort(Exports); 246 llvm::sort(Reexports); 247 llvm::sort(Undefineds); 248 249 TargetList MacOSTargets = {Target(AK_x86_64, PLATFORM_MACOS), 250 Target(AK_arm64, PLATFORM_MACOS)}; 251 252 std::vector<ExportedSymbol> ExpectedExportedSymbols = { 253 {SymbolKind::GlobalSymbol, "_func", false, false, false, MacOSTargets}, 254 {SymbolKind::GlobalSymbol, 255 "_funcFoo", 256 false, 257 false, 258 false, 259 {Target(AK_x86_64, PLATFORM_MACOS)}}, 260 {SymbolKind::GlobalSymbol, "_global", false, false, true, MacOSTargets}, 261 {SymbolKind::GlobalSymbol, 262 "_globalVar", 263 false, 264 false, 265 true, 266 {Target(AK_x86_64, PLATFORM_MACOS)}}, 267 {SymbolKind::ObjectiveCClass, "ClassA", false, false, true, MacOSTargets}, 268 {SymbolKind::ObjectiveCClass, 269 "ClassData", 270 false, 271 false, 272 true, 273 {Target(AK_x86_64, PLATFORM_MACOS)}}, 274 {SymbolKind::ObjectiveCClassEHType, 275 "ClassA", 276 false, 277 false, 278 true, 279 {Target(AK_x86_64, PLATFORM_MACOS)}}, 280 {SymbolKind::ObjectiveCClassEHType, 281 "ClassB", 282 false, 283 false, 284 true, 285 {Target(AK_x86_64, PLATFORM_MACOS)}}, 286 {SymbolKind::ObjectiveCInstanceVariable, 287 "ClassA.ivar1", 288 false, 289 false, 290 true, 291 {Target(AK_x86_64, PLATFORM_MACOS)}}, 292 {SymbolKind::ObjectiveCInstanceVariable, 293 "ClassA.ivar2", 294 false, 295 false, 296 true, 297 {Target(AK_x86_64, PLATFORM_MACOS)}}, 298 {SymbolKind::ObjectiveCInstanceVariable, 299 "ClassC.ivar1", 300 false, 301 false, 302 true, 303 {Target(AK_x86_64, PLATFORM_MACOS)}}, 304 }; 305 std::vector<ExportedSymbol> ExpectedReexportedSymbols = { 306 {SymbolKind::GlobalSymbol, "_funcA", false, false, false, MacOSTargets}, 307 {SymbolKind::GlobalSymbol, "_globalRe", false, false, true, MacOSTargets}, 308 {SymbolKind::ObjectiveCClass, "ClassRexport", false, false, true, 309 MacOSTargets}, 310 }; 311 312 std::vector<ExportedSymbol> ExpectedUndefinedSymbols = { 313 {SymbolKind::GlobalSymbol, 314 "_globalBind", 315 false, 316 false, 317 true, 318 {Target(AK_x86_64, PLATFORM_MACOS)}}, 319 {SymbolKind::GlobalSymbol, 320 "referenced_sym", 321 true, 322 false, 323 true, 324 {Target(AK_x86_64, PLATFORM_MACOS)}}, 325 }; 326 327 EXPECT_EQ(ExpectedExportedSymbols.size(), Exports.size()); 328 EXPECT_EQ(ExpectedReexportedSymbols.size(), Reexports.size()); 329 EXPECT_EQ(ExpectedUndefinedSymbols.size(), Undefineds.size()); 330 EXPECT_TRUE(std::equal(Exports.begin(), Exports.end(), 331 std::begin(ExpectedExportedSymbols))); 332 EXPECT_TRUE(std::equal(Reexports.begin(), Reexports.end(), 333 std::begin(ExpectedReexportedSymbols))); 334 EXPECT_TRUE(std::equal(Undefineds.begin(), Undefineds.end(), 335 std::begin(ExpectedUndefinedSymbols))); 336 } 337 338 TEST(TBDv5, ReadMultipleTargets) { 339 static const char TBDv5File[] = R"({ 340 "tapi_tbd_version": 5, 341 "main_library": { 342 "target_info": [ 343 { 344 "target": "x86_64-macos", 345 "min_deployment": "10.14" 346 }, 347 { 348 "target": "arm64-macos", 349 "min_deployment": "10.14" 350 }, 351 { 352 "target": "arm64-maccatalyst", 353 "min_deployment": "12.1" 354 } 355 ], 356 "install_names":[ 357 { "name":"/usr/lib/libFoo.dylib" } 358 ], 359 "swift_abi":[ { "abi":8 } ], 360 "reexported_libraries": [ 361 { 362 "targets": [ "x86_64-maccatalyst" ], 363 "names": [ 364 "/u/l/l/libfoo.dylib", 365 "/u/l/l/libbar.dylib" 366 ] 367 }, 368 { 369 "targets": [ "arm64-maccatalyst" ], 370 "names": [ "/u/l/l/libArmOnly.dylib" ] 371 } 372 ] 373 } 374 })"; 375 376 Expected<TBDFile> Result = 377 TextAPIReader::get(MemoryBufferRef(TBDv5File, "Test.tbd")); 378 EXPECT_TRUE(!!Result); 379 TBDFile File = std::move(Result.get()); 380 EXPECT_EQ(FileType::TBD_V5, File->getFileType()); 381 EXPECT_EQ(std::string("/usr/lib/libFoo.dylib"), File->getInstallName()); 382 EXPECT_TRUE(File->isApplicationExtensionSafe()); 383 EXPECT_TRUE(File->isTwoLevelNamespace()); 384 EXPECT_EQ(PackedVersion(1, 0, 0), File->getCurrentVersion()); 385 EXPECT_EQ(PackedVersion(1, 0, 0), File->getCompatibilityVersion()); 386 EXPECT_EQ(8U, File->getSwiftABIVersion()); 387 388 TargetList AllTargets = { 389 Target(AK_x86_64, PLATFORM_MACOS, VersionTuple(10, 14)), 390 Target(AK_arm64, PLATFORM_MACOS, VersionTuple(10, 14)), 391 Target(AK_arm64, PLATFORM_MACCATALYST, VersionTuple(12, 1)), 392 }; 393 EXPECT_EQ(mapToPlatformSet(AllTargets), File->getPlatforms()); 394 EXPECT_EQ(mapToArchitectureSet(AllTargets), File->getArchitectures()); 395 396 InterfaceFileRef ReexportA("/u/l/l/libArmOnly.dylib", 397 {Target(AK_arm64, PLATFORM_MACCATALYST)}); 398 InterfaceFileRef ReexportB("/u/l/l/libbar.dylib", 399 {Target(AK_x86_64, PLATFORM_MACCATALYST)}); 400 InterfaceFileRef ReexportC("/u/l/l/libfoo.dylib", 401 {Target(AK_x86_64, PLATFORM_MACCATALYST)}); 402 EXPECT_EQ(3U, File->reexportedLibraries().size()); 403 EXPECT_EQ(ReexportA, File->reexportedLibraries().at(0)); 404 EXPECT_EQ(ReexportB, File->reexportedLibraries().at(1)); 405 EXPECT_EQ(ReexportC, File->reexportedLibraries().at(2)); 406 } 407 408 TEST(TBDv5, ReadMultipleDocuments) { 409 static const char TBDv5File[] = R"({ 410 "tapi_tbd_version": 5, 411 "main_library": { 412 "target_info": [ 413 { 414 "target": "armv7-ios", 415 "min_deployment": "11.0" 416 } 417 ], 418 "install_names":[ 419 { "name":"/S/L/F/Foo.framework/Foo" } 420 ], 421 "reexported_libraries": [ 422 { "names": ["/u/l/l/libfoo.dylib"] } 423 ] 424 }, 425 "libraries": [ 426 { 427 "target_info": [ 428 { 429 "target": "armv7-ios", 430 "min_deployment": "11.0" 431 } 432 ], 433 "install_names":[ 434 { "name":"/u/l/l/libfoo.dylib" } 435 ], 436 "flags":[ 437 { "attributes": ["not_app_extension_safe"] } 438 ], 439 "exported_symbols": [ 440 { 441 "data": { 442 "thread_local": [ "_globalVar" ], 443 "objc_class": [ "ClassData" ], 444 "objc_eh_type": [ "ClassA", "ClassB" ] 445 }, 446 "text": { 447 "global": [ "_funcFoo" ] 448 } 449 } 450 ] 451 } 452 ]})"; 453 454 Expected<TBDFile> Result = 455 TextAPIReader::get(MemoryBufferRef(TBDv5File, "Test.tbd")); 456 EXPECT_TRUE(!!Result); 457 TBDFile File = std::move(Result.get()); 458 EXPECT_EQ(FileType::TBD_V5, File->getFileType()); 459 EXPECT_EQ(std::string("/S/L/F/Foo.framework/Foo"), File->getInstallName()); 460 EXPECT_TRUE(File->isTwoLevelNamespace()); 461 EXPECT_TRUE(File->isApplicationExtensionSafe()); 462 463 TargetList Targets(File->targets().begin(), File->targets().end()); 464 Target iOSTarget(AK_armv7, PLATFORM_IOS, VersionTuple(11, 0)); 465 EXPECT_EQ(TargetList{iOSTarget}, Targets); 466 std::vector<const Symbol *> Symbols(File->symbols().begin(), 467 File->symbols().end()); 468 EXPECT_EQ(0U, Symbols.size()); 469 470 InterfaceFileRef Reexport("/u/l/l/libfoo.dylib", {iOSTarget}); 471 EXPECT_EQ(1U, File->reexportedLibraries().size()); 472 EXPECT_EQ(Reexport, File->reexportedLibraries().at(0)); 473 474 // Check inlined library. 475 EXPECT_EQ(1U, File->documents().size()); 476 TBDReexportFile Document = File->documents().front(); 477 Targets = {Document->targets().begin(), Document->targets().end()}; 478 EXPECT_EQ(TargetList{iOSTarget}, Targets); 479 EXPECT_EQ(std::string("/u/l/l/libfoo.dylib"), Document->getInstallName()); 480 EXPECT_EQ(0U, Document->getSwiftABIVersion()); 481 EXPECT_TRUE(Document->isTwoLevelNamespace()); 482 EXPECT_FALSE(Document->isApplicationExtensionSafe()); 483 484 ExportedSymbolSeq Exports; 485 for (const auto *Sym : Document->symbols()) 486 Exports.emplace_back( 487 ExportedSymbol{Sym->getKind(), 488 std::string(Sym->getName()), 489 Sym->isWeakDefined() || Sym->isWeakReferenced(), 490 Sym->isThreadLocalValue(), 491 Sym->isData(), 492 {iOSTarget}}); 493 494 llvm::sort(Exports); 495 ExportedSymbolSeq ExpectedExports = { 496 {SymbolKind::GlobalSymbol, "_funcFoo", false, false, false, {iOSTarget}}, 497 {SymbolKind::GlobalSymbol, "_globalVar", false, true, true, {iOSTarget}}, 498 {SymbolKind::ObjectiveCClass, 499 "ClassData", 500 false, 501 false, 502 true, 503 {iOSTarget}}, 504 {SymbolKind::ObjectiveCClassEHType, 505 "ClassA", 506 false, 507 false, 508 true, 509 {iOSTarget}}, 510 {SymbolKind::ObjectiveCClassEHType, 511 "ClassB", 512 false, 513 false, 514 true, 515 {iOSTarget}}, 516 }; 517 518 EXPECT_EQ(ExpectedExports.size(), Exports.size()); 519 EXPECT_TRUE( 520 std::equal(Exports.begin(), Exports.end(), std::begin(ExpectedExports))); 521 } 522 523 } // end namespace TBDv5 524