[llvm] 16c1f43 - [TextAPI] Add functionality to manipulate over InterfaceFiles

Cyndy Ishida via llvm-commits llvm-commits at lists.llvm.org
Tue Jul 25 09:24:50 PDT 2023


Author: Cyndy Ishida
Date: 2023-07-25T09:23:57-07:00
New Revision: 16c1f43642e40dbc0d59fcf68ed1682dbf4d87bd

URL: https://github.com/llvm/llvm-project/commit/16c1f43642e40dbc0d59fcf68ed1682dbf4d87bd
DIFF: https://github.com/llvm/llvm-project/commit/16c1f43642e40dbc0d59fcf68ed1682dbf4d87bd.diff

LOG: [TextAPI] Add functionality to manipulate over InterfaceFiles

InterfaceFile is the in-memory representation for tbd files. Add APIs to
merge, extract, remove, and inline reexported libraries.

Reviewed By: zixuw

Differential Revision: https://reviews.llvm.org/D153398

Added: 
    llvm/include/llvm/TextAPI/TextAPIError.h
    llvm/lib/TextAPI/TextAPIError.cpp

Modified: 
    llvm/include/llvm/TextAPI/ArchitectureSet.h
    llvm/include/llvm/TextAPI/InterfaceFile.h
    llvm/include/llvm/TextAPI/Symbol.h
    llvm/lib/TextAPI/CMakeLists.txt
    llvm/lib/TextAPI/InterfaceFile.cpp
    llvm/unittests/TextAPI/TextStubV5Tests.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/TextAPI/ArchitectureSet.h b/llvm/include/llvm/TextAPI/ArchitectureSet.h
index f17cb74c91839b..2cce9dbf0d80c3 100644
--- a/llvm/include/llvm/TextAPI/ArchitectureSet.h
+++ b/llvm/include/llvm/TextAPI/ArchitectureSet.h
@@ -40,13 +40,18 @@ class ArchitectureSet {
   ArchitectureSet(Architecture Arch) : ArchitectureSet() { set(Arch); }
   ArchitectureSet(const std::vector<Architecture> &Archs);
 
+  static ArchitectureSet All() { return ArchitectureSet(EndIndexVal); }
+
   void set(Architecture Arch) {
     if (Arch == AK_unknown)
       return;
     ArchSet |= 1U << static_cast<int>(Arch);
   }
 
-  void clear(Architecture Arch) { ArchSet &= ~(1U << static_cast<int>(Arch)); }
+  ArchitectureSet clear(Architecture Arch) {
+    ArchSet &= ~(1U << static_cast<int>(Arch));
+    return ArchSet;
+  }
 
   bool has(Architecture Arch) const {
     return ArchSet & (1U << static_cast<int>(Arch));

diff  --git a/llvm/include/llvm/TextAPI/InterfaceFile.h b/llvm/include/llvm/TextAPI/InterfaceFile.h
index 2f89605d24baec..7966ec8e70cdff 100644
--- a/llvm/include/llvm/TextAPI/InterfaceFile.h
+++ b/llvm/include/llvm/TextAPI/InterfaceFile.h
@@ -372,6 +372,37 @@ class InterfaceFile {
     return SymbolsSet->undefineds();
   };
 
+  /// Extract architecture slice from Interface.
+  ///
+  /// \param Arch architecture to extract from.
+  /// \return New InterfaceFile with extracted architecture slice.
+  llvm::Expected<std::unique_ptr<InterfaceFile>>
+  extract(Architecture Arch) const;
+
+  /// Remove architecture slice from Interface.
+  ///
+  /// \param Arch architecture to remove.
+  /// \return New Interface File with removed architecture slice.
+  llvm::Expected<std::unique_ptr<InterfaceFile>>
+  remove(Architecture Arch) const;
+
+  /// Merge Interfaces for the same library. The following library attributes
+  /// must match.
+  /// * Install name, Current & Compatibility version,
+  /// * Two-level namespace enablement, and App extension enablement.
+  ///
+  /// \param O The Interface to merge.
+  /// \return New Interface File that was merged.
+  llvm::Expected<std::unique_ptr<InterfaceFile>>
+  merge(const InterfaceFile *O) const;
+
+  /// Inline reexported library into Interface.
+  ///
+  /// \param Library Interface of reexported library.
+  /// \param Overwrite Whether to overwrite preexisting inlined library.
+  void inlineLibrary(std::shared_ptr<InterfaceFile> Library,
+                     bool Overwrite = false);
+
   /// The equality is determined by attributes that impact linking
   /// compatibilities. Path, & FileKind are irrelevant since these by
   /// itself should not impact linking.

diff  --git a/llvm/include/llvm/TextAPI/Symbol.h b/llvm/include/llvm/TextAPI/Symbol.h
index a20fcc785b4097..63c21c50e9c39c 100644
--- a/llvm/include/llvm/TextAPI/Symbol.h
+++ b/llvm/include/llvm/TextAPI/Symbol.h
@@ -121,6 +121,10 @@ class Symbol {
     return (Flags & SymbolFlags::Text) == SymbolFlags::Text;
   }
 
+  bool hasArchitecture(Architecture Arch) const {
+    return mapToArchitectureSet(Targets).contains(Arch);
+  }
+
   using const_target_iterator = TargetList::const_iterator;
   using const_target_range = llvm::iterator_range<const_target_iterator>;
   const_target_range targets() const { return {Targets}; }

diff  --git a/llvm/include/llvm/TextAPI/TextAPIError.h b/llvm/include/llvm/TextAPI/TextAPIError.h
new file mode 100644
index 00000000000000..566890db4b89a8
--- /dev/null
+++ b/llvm/include/llvm/TextAPI/TextAPIError.h
@@ -0,0 +1,38 @@
+//===- llvm/TextAPI/TextAPIError.h - TAPI Error -----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Define TAPI specific error codes.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TEXTAPI_TEXTAPIERROR_H
+#define LLVM_TEXTAPI_TEXTAPIERROR_H
+
+#include "llvm/Support/Error.h"
+
+namespace llvm::MachO {
+enum class TextAPIErrorCode {
+  NoSuchArchitecture,
+  EmptyResults,
+  GenericFrontendError,
+};
+
+class TextAPIError : public llvm::ErrorInfo<TextAPIError> {
+public:
+  static char ID;
+  TextAPIErrorCode EC;
+
+  TextAPIError(TextAPIErrorCode EC) : EC(EC) {}
+
+  void log(raw_ostream &OS) const override;
+  std::error_code convertToErrorCode() const override;
+};
+
+} // namespace llvm::MachO
+#endif // LLVM_TEXTAPI_TEXTAPIERROR_H

diff  --git a/llvm/lib/TextAPI/CMakeLists.txt b/llvm/lib/TextAPI/CMakeLists.txt
index 647a04f5da7881..4088757afb900e 100644
--- a/llvm/lib/TextAPI/CMakeLists.txt
+++ b/llvm/lib/TextAPI/CMakeLists.txt
@@ -8,6 +8,7 @@ add_llvm_component_library(LLVMTextAPI
   Symbol.cpp
   SymbolSet.cpp
   Target.cpp
+  TextAPIError.cpp
   TextStub.cpp
   TextStubCommon.cpp
 

diff  --git a/llvm/lib/TextAPI/InterfaceFile.cpp b/llvm/lib/TextAPI/InterfaceFile.cpp
index b7f967aa754e78..b9ea02d8e097e3 100644
--- a/llvm/lib/TextAPI/InterfaceFile.cpp
+++ b/llvm/lib/TextAPI/InterfaceFile.cpp
@@ -11,6 +11,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/TextAPI/InterfaceFile.h"
+#include "llvm/TextAPI/TextAPIError.h"
 #include <iomanip>
 #include <sstream>
 
@@ -81,6 +82,267 @@ void InterfaceFile::addDocument(std::shared_ptr<InterfaceFile> &&Document) {
   Documents.insert(Pos, Document);
 }
 
+void InterfaceFile::inlineLibrary(std::shared_ptr<InterfaceFile> Library,
+                                  bool Overwrite) {
+  auto AddFwk = [&](std::shared_ptr<InterfaceFile> &&Reexport) {
+    auto It = lower_bound(
+        Documents, Reexport->getInstallName(),
+        [](std::shared_ptr<InterfaceFile> &Lhs, const StringRef Rhs) {
+          return Lhs->getInstallName() < Rhs;
+        });
+
+    if (Overwrite && It != Documents.end() &&
+        Reexport->getInstallName() == (*It)->getInstallName()) {
+      std::replace(Documents.begin(), Documents.end(), *It,
+                   std::move(Reexport));
+      return;
+    }
+
+    if ((It != Documents.end()) &&
+        !(Reexport->getInstallName() < (*It)->getInstallName()))
+      return;
+
+    Documents.emplace(It, std::move(Reexport));
+  };
+  for (auto Doc : Library->documents())
+    AddFwk(std::move(Doc));
+
+  Library->Documents.clear();
+  AddFwk(std::move(Library));
+}
+
+Expected<std::unique_ptr<InterfaceFile>>
+InterfaceFile::merge(const InterfaceFile *O) const {
+  // Verify files can be merged.
+  if (getInstallName() != O->getInstallName()) {
+    return make_error<StringError>("install names do not match",
+                                   inconvertibleErrorCode());
+  }
+
+  if (getCurrentVersion() != O->getCurrentVersion()) {
+    return make_error<StringError>("current versions do not match",
+                                   inconvertibleErrorCode());
+  }
+
+  if (getCompatibilityVersion() != O->getCompatibilityVersion()) {
+    return make_error<StringError>("compatibility versions do not match",
+                                   inconvertibleErrorCode());
+  }
+
+  if ((getSwiftABIVersion() != 0) && (O->getSwiftABIVersion() != 0) &&
+      (getSwiftABIVersion() != O->getSwiftABIVersion())) {
+    return make_error<StringError>("swift ABI versions do not match",
+                                   inconvertibleErrorCode());
+  }
+
+  if (isTwoLevelNamespace() != O->isTwoLevelNamespace()) {
+    return make_error<StringError>("two level namespace flags do not match",
+                                   inconvertibleErrorCode());
+  }
+
+  if (isApplicationExtensionSafe() != O->isApplicationExtensionSafe()) {
+    return make_error<StringError>(
+        "application extension safe flags do not match",
+        inconvertibleErrorCode());
+  }
+
+  std::unique_ptr<InterfaceFile> IF(new InterfaceFile());
+  IF->setFileType(std::max(getFileType(), O->getFileType()));
+  IF->setPath(getPath());
+  IF->setInstallName(getInstallName());
+  IF->setCurrentVersion(getCurrentVersion());
+  IF->setCompatibilityVersion(getCompatibilityVersion());
+
+  if (getSwiftABIVersion() == 0)
+    IF->setSwiftABIVersion(O->getSwiftABIVersion());
+  else
+    IF->setSwiftABIVersion(getSwiftABIVersion());
+
+  IF->setTwoLevelNamespace(isTwoLevelNamespace());
+  IF->setApplicationExtensionSafe(isApplicationExtensionSafe());
+
+  for (const auto &It : umbrellas()) {
+    if (!It.second.empty())
+      IF->addParentUmbrella(It.first, It.second);
+  }
+  for (const auto &It : O->umbrellas()) {
+    if (!It.second.empty())
+      IF->addParentUmbrella(It.first, It.second);
+  }
+  IF->addTargets(targets());
+  IF->addTargets(O->targets());
+
+  for (const auto &Lib : allowableClients())
+    for (const auto &Target : Lib.targets())
+      IF->addAllowableClient(Lib.getInstallName(), Target);
+
+  for (const auto &Lib : O->allowableClients())
+    for (const auto &Target : Lib.targets())
+      IF->addAllowableClient(Lib.getInstallName(), Target);
+
+  for (const auto &Lib : reexportedLibraries())
+    for (const auto &Target : Lib.targets())
+      IF->addReexportedLibrary(Lib.getInstallName(), Target);
+
+  for (const auto &Lib : O->reexportedLibraries())
+    for (const auto &Target : Lib.targets())
+      IF->addReexportedLibrary(Lib.getInstallName(), Target);
+
+  for (const auto &[Target, Path] : rpaths())
+    IF->addRPath(Target, Path);
+  for (const auto &[Target, Path] : O->rpaths())
+    IF->addRPath(Target, Path);
+
+  for (const auto *Sym : symbols()) {
+    IF->addSymbol(Sym->getKind(), Sym->getName(), Sym->targets(),
+                  Sym->getFlags());
+  }
+
+  for (const auto *Sym : O->symbols()) {
+    IF->addSymbol(Sym->getKind(), Sym->getName(), Sym->targets(),
+                  Sym->getFlags());
+  }
+
+  return std::move(IF);
+}
+
+Expected<std::unique_ptr<InterfaceFile>>
+InterfaceFile::remove(Architecture Arch) const {
+  if (getArchitectures() == Arch)
+    return make_error<StringError>("cannot remove last architecture slice '" +
+                                       getArchitectureName(Arch) + "'",
+                                   inconvertibleErrorCode());
+
+  if (!getArchitectures().has(Arch)) {
+    bool Found = false;
+    for (auto &Doc : Documents) {
+      if (Doc->getArchitectures().has(Arch)) {
+        Found = true;
+        break;
+      }
+    }
+
+    if (!Found)
+      return make_error<TextAPIError>(TextAPIErrorCode::NoSuchArchitecture);
+  }
+
+  std::unique_ptr<InterfaceFile> IF(new InterfaceFile());
+  IF->setFileType(getFileType());
+  IF->setPath(getPath());
+  IF->addTargets(targets(ArchitectureSet::All().clear(Arch)));
+  IF->setInstallName(getInstallName());
+  IF->setCurrentVersion(getCurrentVersion());
+  IF->setCompatibilityVersion(getCompatibilityVersion());
+  IF->setSwiftABIVersion(getSwiftABIVersion());
+  IF->setTwoLevelNamespace(isTwoLevelNamespace());
+  IF->setApplicationExtensionSafe(isApplicationExtensionSafe());
+  for (const auto &It : umbrellas())
+    if (It.first.Arch != Arch)
+      IF->addParentUmbrella(It.first, It.second);
+
+  for (const auto &Lib : allowableClients()) {
+    for (const auto &Target : Lib.targets())
+      if (Target.Arch != Arch)
+        IF->addAllowableClient(Lib.getInstallName(), Target);
+  }
+
+  for (const auto &Lib : reexportedLibraries()) {
+    for (const auto &Target : Lib.targets())
+      if (Target.Arch != Arch)
+        IF->addReexportedLibrary(Lib.getInstallName(), Target);
+  }
+
+  for (const auto *Sym : symbols()) {
+    auto Archs = Sym->getArchitectures();
+    Archs.clear(Arch);
+    if (Archs.empty())
+      continue;
+
+    IF->addSymbol(Sym->getKind(), Sym->getName(), Sym->targets(Archs),
+                  Sym->getFlags());
+  }
+
+  for (auto &Doc : Documents) {
+    // Skip the inlined document if the to be removed architecture is the
+    // only one left.
+    if (Doc->getArchitectures() == Arch)
+      continue;
+
+    // If the document doesn't contain the arch, then no work is to be done
+    // and it can be copied over.
+    if (!Doc->getArchitectures().has(Arch)) {
+      auto NewDoc = Doc;
+      IF->addDocument(std::move(NewDoc));
+      continue;
+    }
+
+    auto Result = Doc->remove(Arch);
+    if (!Result)
+      return Result;
+
+    IF->addDocument(std::move(Result.get()));
+  }
+
+  return std::move(IF);
+}
+
+Expected<std::unique_ptr<InterfaceFile>>
+InterfaceFile::extract(Architecture Arch) const {
+  if (!getArchitectures().has(Arch)) {
+    return make_error<StringError>("file doesn't have architecture '" +
+                                       getArchitectureName(Arch) + "'",
+                                   inconvertibleErrorCode());
+  }
+
+  std::unique_ptr<InterfaceFile> IF(new InterfaceFile());
+  IF->setFileType(getFileType());
+  IF->setPath(getPath());
+  IF->addTargets(targets(Arch));
+  IF->setInstallName(getInstallName());
+  IF->setCurrentVersion(getCurrentVersion());
+  IF->setCompatibilityVersion(getCompatibilityVersion());
+  IF->setSwiftABIVersion(getSwiftABIVersion());
+  IF->setTwoLevelNamespace(isTwoLevelNamespace());
+  IF->setApplicationExtensionSafe(isApplicationExtensionSafe());
+  for (const auto &It : umbrellas())
+    if (It.first.Arch == Arch)
+      IF->addParentUmbrella(It.first, It.second);
+
+  for (const auto &It : rpaths())
+    if (It.first.Arch == Arch)
+      IF->addRPath(It.first, It.second);
+
+  for (const auto &Lib : allowableClients())
+    for (const auto &Target : Lib.targets())
+      if (Target.Arch == Arch)
+        IF->addAllowableClient(Lib.getInstallName(), Target);
+
+  for (const auto &Lib : reexportedLibraries())
+    for (const auto &Target : Lib.targets())
+      if (Target.Arch == Arch)
+        IF->addReexportedLibrary(Lib.getInstallName(), Target);
+
+  for (const auto *Sym : symbols()) {
+    if (Sym->hasArchitecture(Arch))
+      IF->addSymbol(Sym->getKind(), Sym->getName(), Sym->targets(Arch),
+                    Sym->getFlags());
+  }
+
+  for (auto &Doc : Documents) {
+    // Skip documents that don't have the requested architecture.
+    if (!Doc->getArchitectures().has(Arch))
+      continue;
+
+    auto Result = Doc->extract(Arch);
+    if (!Result)
+      return Result;
+
+    IF->addDocument(std::move(Result.get()));
+  }
+
+  return std::move(IF);
+}
+
 static bool isYAMLTextStub(const FileType &Kind) {
   return (Kind >= FileType::TBD_V1) && (Kind < FileType::TBD_V5);
 }

diff  --git a/llvm/lib/TextAPI/TextAPIError.cpp b/llvm/lib/TextAPI/TextAPIError.cpp
new file mode 100644
index 00000000000000..07eae7ab4100bc
--- /dev/null
+++ b/llvm/lib/TextAPI/TextAPIError.cpp
@@ -0,0 +1,33 @@
+//===- TextAPIError.cpp - Tapi Error ----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Implements TAPI Error.
+///
+//===----------------------------------------------------------------------===//
+
+#include "llvm/TextAPI/TextAPIError.h"
+
+using namespace llvm;
+using namespace llvm::MachO;
+
+char TextAPIError::ID = 0;
+
+void TextAPIError::log(raw_ostream &OS) const {
+  switch (EC) {
+  case TextAPIErrorCode::NoSuchArchitecture:
+    OS << "no such architecture\n";
+    return;
+  default:
+    llvm_unreachable("unhandled TextAPIErrorCode");
+  }
+}
+
+std::error_code TextAPIError::convertToErrorCode() const {
+  llvm_unreachable("convertToErrorCode is not supported.");
+}

diff  --git a/llvm/unittests/TextAPI/TextStubV5Tests.cpp b/llvm/unittests/TextAPI/TextStubV5Tests.cpp
index 782b32444ae33b..1ccbfe2b1a27f6 100644
--- a/llvm/unittests/TextAPI/TextStubV5Tests.cpp
+++ b/llvm/unittests/TextAPI/TextStubV5Tests.cpp
@@ -1104,4 +1104,1145 @@ TEST(TBDv5, InvalidSymbols) {
   EXPECT_EQ("invalid exported_symbols section\n", ErrorMessage);
 }
 
+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 = {
+      {SymbolKind::GlobalSymbol, "_func", false, false, false, MacOSTargets},
+      {SymbolKind::GlobalSymbol,
+       "_funcFoo",
+       false,
+       false,
+       false,
+       {Target(AK_x86_64, PLATFORM_MACOS)}},
+      {SymbolKind::GlobalSymbol,
+       "_funcFooZ",
+       false,
+       false,
+       false,
+       {Target(AK_x86_64, PLATFORM_MACOS)}},
+      {SymbolKind::GlobalSymbol, "_funcZ", false, false, false, MacOSTargets},
+      {SymbolKind::GlobalSymbol, "_global", false, false, true, MacOSTargets},
+      {SymbolKind::GlobalSymbol,
+       "_globalVar",
+       false,
+       false,
+       true,
+       {Target(AK_x86_64, PLATFORM_MACOS)}},
+      {SymbolKind::GlobalSymbol,
+       "_globalVarZ",
+       false,
+       false,
+       true,
+       {Target(AK_x86_64, PLATFORM_MACOS)}},
+      {SymbolKind::GlobalSymbol, "_globalZ", false, false, true, MacOSTargets},
+      {SymbolKind::ObjectiveCClass,
+       "ClassA",
+       false,
+       false,
+       true,
+       {Target(AK_x86_64, PLATFORM_MACOS)}},
+      {SymbolKind::ObjectiveCClass,
+       "ClassB",
+       false,
+       false,
+       true,
+       {Target(AK_x86_64, PLATFORM_MACOS)}},
+      {SymbolKind::ObjectiveCClass,
+       "ClassData",
+       false,
+       false,
+       true,
+       {Target(AK_x86_64, PLATFORM_MACOS)}},
+      {SymbolKind::ObjectiveCClass,
+       "ClassF",
+       false,
+       false,
+       true,
+       {Target(AK_x86_64, PLATFORM_MACOS)}},
+      {SymbolKind::ObjectiveCClass,
+       "ClassZ",
+       false,
+       false,
+       true,
+       {Target(AK_x86_64, PLATFORM_MACOS)}},
+      {SymbolKind::ObjectiveCClassEHType,
+       "ClassA",
+       false,
+       false,
+       true,
+       {Target(AK_x86_64, PLATFORM_MACOS)}},
+      {SymbolKind::ObjectiveCClassEHType,
+       "ClassB",
+       false,
+       false,
+       true,
+       {Target(AK_x86_64, PLATFORM_MACOS)}},
+      {SymbolKind::ObjectiveCClassEHType,
+       "ClassF",
+       false,
+       false,
+       true,
+       {Target(AK_x86_64, PLATFORM_MACOS)}},
+      {SymbolKind::ObjectiveCClassEHType,
+       "ClassZ",
+       false,
+       false,
+       true,
+       {Target(AK_x86_64, PLATFORM_MACOS)}},
+      {SymbolKind::ObjectiveCInstanceVariable,
+       "ClassA.ivar1",
+       false,
+       false,
+       true,
+       {Target(AK_x86_64, PLATFORM_MACOS)}},
+      {SymbolKind::ObjectiveCInstanceVariable,
+       "ClassA.ivar2",
+       false,
+       false,
+       true,
+       {Target(AK_x86_64, PLATFORM_MACOS)}},
+      {SymbolKind::ObjectiveCInstanceVariable,
+       "ClassC.ivar1",
+       false,
+       false,
+       true,
+       {Target(AK_x86_64, PLATFORM_MACOS)}},
+      {SymbolKind::ObjectiveCInstanceVariable,
+       "ClassF.ivar1",
+       false,
+       false,
+       true,
+       {Target(AK_x86_64, PLATFORM_MACOS)}},
+      {SymbolKind::ObjectiveCInstanceVariable,
+       "ClassZ.ivar1",
+       false,
+       false,
+       true,
+       {Target(AK_x86_64, PLATFORM_MACOS)}},
+      {SymbolKind::ObjectiveCInstanceVariable,
+       "ClassZ.ivar2",
+       false,
+       false,
+       true,
+       {Target(AK_x86_64, PLATFORM_MACOS)}},
+  };
+
+  std::vector<ExportedSymbol> ExpectedReexportedSymbols = {
+      {SymbolKind::GlobalSymbol, "_funcA", false, false, false, MacOSTargets},
+      {SymbolKind::GlobalSymbol, "_globalRe", false, false, true, MacOSTargets},
+      {SymbolKind::ObjectiveCClass, "ClassRexport", false, false, true,
+       MacOSTargets},
+  };
+
+  std::vector<ExportedSymbol> ExpectedUndefinedSymbols = {
+      {SymbolKind::GlobalSymbol,
+       "_globalBind",
+       false,
+       false,
+       true,
+       {Target(AK_x86_64, PLATFORM_MACOS)}},
+      {SymbolKind::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 = {
+      {SymbolKind::GlobalSymbol, "_func", false, false, false, MacOSTargets},
+      {SymbolKind::GlobalSymbol, "_global", false, false, true, MacOSTargets},
+      {SymbolKind::ObjectiveCClass, "ClassA", false, false, true, MacOSTargets},
+  };
+  std::vector<ExportedSymbol> ExpectedReexportedSymbols = {
+      {SymbolKind::GlobalSymbol, "_funcA", false, false, false, MacOSTargets},
+      {SymbolKind::GlobalSymbol, "_globalRe", false, false, true, MacOSTargets},
+      {SymbolKind::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"
+        ]
+    }
+  ],
+  "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_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 = {
+      {SymbolKind::GlobalSymbol, "_func", false, false, false, MacOSTargets},
+      {SymbolKind::GlobalSymbol, "_global", false, false, true, MacOSTargets},
+      {SymbolKind::ObjectiveCClass, "ClassA", false, false, true, MacOSTargets},
+  };
+  std::vector<ExportedSymbol> ExpectedReexportedSymbols = {
+      {SymbolKind::GlobalSymbol, "_funcA", false, false, false, MacOSTargets},
+      {SymbolKind::GlobalSymbol, "_globalRe", false, false, true, MacOSTargets},
+      {SymbolKind::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 = {
+      {SymbolKind::GlobalSymbol, "_global", false, false, true, AllTargets},
+      {SymbolKind::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


        


More information about the llvm-commits mailing list