[llvm] 38013ad - [TextAPI] Add option to filter out unsupported/unknown/invalid targets (#195161)

via llvm-commits llvm-commits at lists.llvm.org
Mon May 4 08:56:31 PDT 2026


Author: Cyndy Ishida
Date: 2026-05-04T08:56:25-07:00
New Revision: 38013ad8fe94c62641e246079f74db0747fb4bf2

URL: https://github.com/llvm/llvm-project/commit/38013ad8fe94c62641e246079f74db0747fb4bf2
DIFF: https://github.com/llvm/llvm-project/commit/38013ad8fe94c62641e246079f74db0747fb4bf2.diff

LOG: [TextAPI] Add option to filter out unsupported/unknown/invalid targets (#195161)

Pipe `SkipUnknownTriples` flag through TBD file YAML and v5 JSON
parsers.
When set, target strings with an unknown architecture or platform are
dropped from the parsed `InterfaceFile` instead of producing a fatal
parse error.

Additionally, "unknown architecture"/"unknown platform" parse errors are
collapsed into a single "unknown target" message.

  resolves: rdar://175690963

Added: 
    

Modified: 
    llvm/include/llvm/Object/TapiUniversal.h
    llvm/include/llvm/TextAPI/Target.h
    llvm/include/llvm/TextAPI/TextAPIReader.h
    llvm/lib/Object/TapiUniversal.cpp
    llvm/lib/TextAPI/TextAPIContext.h
    llvm/lib/TextAPI/TextStub.cpp
    llvm/lib/TextAPI/TextStubV5.cpp
    llvm/unittests/TextAPI/TextStubHelpers.h
    llvm/unittests/TextAPI/TextStubV3Tests.cpp
    llvm/unittests/TextAPI/TextStubV4Tests.cpp
    llvm/unittests/TextAPI/TextStubV5Tests.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/Object/TapiUniversal.h b/llvm/include/llvm/Object/TapiUniversal.h
index 3cc2060dc46ec..92df5e3ed42a7 100644
--- a/llvm/include/llvm/Object/TapiUniversal.h
+++ b/llvm/include/llvm/Object/TapiUniversal.h
@@ -90,9 +90,9 @@ class LLVM_ABI TapiUniversal : public Binary {
     }
   };
 
-  TapiUniversal(MemoryBufferRef Source, Error &Err);
+  TapiUniversal(MemoryBufferRef Source, bool SkipUnknownTriples, Error &Err);
   static Expected<std::unique_ptr<TapiUniversal>>
-  create(MemoryBufferRef Source);
+  create(MemoryBufferRef Source, bool SkipUnknownTriples = false);
   ~TapiUniversal() override;
 
   object_iterator begin_objects() const { return ObjectForArch(this, 0); }

diff  --git a/llvm/include/llvm/TextAPI/Target.h b/llvm/include/llvm/TextAPI/Target.h
index 5cc507fe21f78..5f2b4a1f02553 100644
--- a/llvm/include/llvm/TextAPI/Target.h
+++ b/llvm/include/llvm/TextAPI/Target.h
@@ -38,6 +38,10 @@ class Target {
 
   LLVM_ABI static llvm::Expected<Target> create(StringRef Target);
 
+  LLVM_ABI bool isValid() const {
+    return Arch != AK_unknown && Platform != PLATFORM_UNKNOWN;
+  }
+
   LLVM_ABI operator std::string() const;
 
   Architecture Arch;

diff  --git a/llvm/include/llvm/TextAPI/TextAPIReader.h b/llvm/include/llvm/TextAPI/TextAPIReader.h
index 603b24b47283d..b2de31d82ccbb 100644
--- a/llvm/include/llvm/TextAPI/TextAPIReader.h
+++ b/llvm/include/llvm/TextAPI/TextAPIReader.h
@@ -36,8 +36,9 @@ class TextAPIReader {
   /// library.
   ///
   /// \param InputBuffer Buffer holding contents of TAPI text file.
+  /// \param SkipUnknownTriples Whether to ignore unknown or invalid triples.
   LLVM_ABI static Expected<std::unique_ptr<InterfaceFile>>
-  get(MemoryBufferRef InputBuffer);
+  get(MemoryBufferRef InputBuffer, bool SkipUnknownTriples = false);
 
   TextAPIReader() = delete;
 };

diff  --git a/llvm/lib/Object/TapiUniversal.cpp b/llvm/lib/Object/TapiUniversal.cpp
index 52f3a04d088f5..6ed7c27e0ce53 100644
--- a/llvm/lib/Object/TapiUniversal.cpp
+++ b/llvm/lib/Object/TapiUniversal.cpp
@@ -19,9 +19,11 @@ using namespace llvm;
 using namespace MachO;
 using namespace object;
 
-TapiUniversal::TapiUniversal(MemoryBufferRef Source, Error &Err)
+TapiUniversal::TapiUniversal(MemoryBufferRef Source, bool SkipUnknownTriples,
+                             Error &Err)
     : Binary(ID_TapiUniversal, Source) {
-  Expected<std::unique_ptr<InterfaceFile>> Result = TextAPIReader::get(Source);
+  Expected<std::unique_ptr<InterfaceFile>> Result =
+      TextAPIReader::get(Source, SkipUnknownTriples);
   ErrorAsOutParameter ErrAsOuParam(Err);
   if (!Result) {
     Err = Result.takeError();
@@ -60,9 +62,10 @@ TapiUniversal::ObjectForArch::getAsObjectFile() const {
 }
 
 Expected<std::unique_ptr<TapiUniversal>>
-TapiUniversal::create(MemoryBufferRef Source) {
+TapiUniversal::create(MemoryBufferRef Source, bool SkipUnknownTriples) {
   Error Err = Error::success();
-  std::unique_ptr<TapiUniversal> Ret(new TapiUniversal(Source, Err));
+  std::unique_ptr<TapiUniversal> Ret(
+      new TapiUniversal(Source, SkipUnknownTriples, Err));
   if (Err)
     return std::move(Err);
   return std::move(Ret);

diff  --git a/llvm/lib/TextAPI/TextAPIContext.h b/llvm/lib/TextAPI/TextAPIContext.h
index 3151db7b6b80e..a54553186775e 100644
--- a/llvm/lib/TextAPI/TextAPIContext.h
+++ b/llvm/lib/TextAPI/TextAPIContext.h
@@ -23,6 +23,7 @@ struct TextAPIContext {
   std::string ErrorMessage;
   std::string Path;
   FileType FileKind;
+  bool SkipUnknownTriples;
 };
 
 } // end namespace MachO.

diff  --git a/llvm/lib/TextAPI/TextStub.cpp b/llvm/lib/TextAPI/TextStub.cpp
index be9be07a2aa0f..e5b714ef88237 100644
--- a/llvm/lib/TextAPI/TextStub.cpp
+++ b/llvm/lib/TextAPI/TextStub.cpp
@@ -13,6 +13,7 @@
 #include "TextAPIContext.h"
 #include "TextStubCommon.h"
 #include "llvm/ADT/BitmaskEnum.h"
+#include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Allocator.h"
@@ -313,6 +314,12 @@ template <> struct MappingTraits<UndefinedSection> {
 template <> struct MappingTraits<SymbolSection> {
   static void mapping(IO &IO, SymbolSection &Section) {
     IO.mapRequired("targets", Section.Targets);
+    // With SkipUnknownTriples, ScalarTraits of Target accepts unknown
+    // arch/platform scalars without erroring, leaving invalid Targets in the
+    // vector. Drop them so downstream code only sees valid Targets.
+    if (!IO.outputting())
+      llvm::erase_if(Section.Targets,
+                     [](const Target &T) { return !T.isValid(); });
     IO.mapOptional("symbols", Section.Symbols);
     IO.mapOptional("objc-classes", Section.Classes);
     IO.mapOptional("objc-eh-types", Section.ClassEHs);
@@ -325,6 +332,9 @@ template <> struct MappingTraits<SymbolSection> {
 template <> struct MappingTraits<UmbrellaSection> {
   static void mapping(IO &IO, UmbrellaSection &Section) {
     IO.mapRequired("targets", Section.Targets);
+    if (!IO.outputting())
+      llvm::erase_if(Section.Targets,
+                     [](const Target &T) { return !T.isValid(); });
     IO.mapRequired("umbrella", Section.Umbrella);
   }
 };
@@ -341,6 +351,9 @@ struct MappingContextTraits<MetadataSection, MetadataSection::Option> {
   static void mapping(IO &IO, MetadataSection &Section,
                       MetadataSection::Option &OptionKind) {
     IO.mapRequired("targets", Section.Targets);
+    if (!IO.outputting())
+      llvm::erase_if(Section.Targets,
+                     [](const Target &T) { return !T.isValid(); });
     switch (OptionKind) {
     case MetadataSection::Option::Clients:
       IO.mapRequired("clients", Section.Values);
@@ -377,7 +390,7 @@ template <> struct ScalarTraits<Target> {
     }
   }
 
-  static StringRef input(StringRef Scalar, void *, Target &Value) {
+  static StringRef input(StringRef Scalar, void *Ctx, Target &Value) {
     auto Result = Target::create(Scalar);
     if (!Result) {
       consumeError(Result.takeError());
@@ -385,10 +398,11 @@ template <> struct ScalarTraits<Target> {
     }
 
     Value = *Result;
-    if (Value.Arch == AK_unknown)
-      return "unknown architecture";
-    if (Value.Platform == PLATFORM_UNKNOWN)
-      return "unknown platform";
+
+    const bool SkipUnknownTriples =
+        reinterpret_cast<TextAPIContext *>(Ctx)->SkipUnknownTriples;
+    if (!Value.isValid() && !SkipUnknownTriples)
+      return "unknown target";
 
     return {};
   }
@@ -563,7 +577,10 @@ template <> struct MappingTraits<const InterfaceFile *> {
           if ((Architecture == AK_i386) && (Platform == PLATFORM_MACCATALYST))
             continue;
 
-          Targets.emplace_back(Architecture, Platform);
+          Target T(Architecture, Platform);
+          if (!T.isValid())
+            continue;
+          Targets.push_back(T);
         }
       }
       return Targets;
@@ -602,6 +619,8 @@ template <> struct MappingTraits<const InterfaceFile *> {
       for (const auto &Section : Exports) {
         const auto Targets =
             synthesizeTargets(Section.Architectures, Platforms);
+        if (Targets.empty())
+          continue;
 
         for (const auto &Lib : Section.AllowableClients)
           for (const auto &Target : Targets)
@@ -646,6 +665,8 @@ template <> struct MappingTraits<const InterfaceFile *> {
       for (const auto &Section : Undefineds) {
         const auto Targets =
             synthesizeTargets(Section.Architectures, Platforms);
+        if (Targets.empty())
+          continue;
         for (auto &Symbol : Section.Symbols) {
           if (Ctx->FileKind != FileType::TBD_V3 &&
               Symbol.value.starts_with(ObjC2EHTypePrefix))
@@ -769,8 +790,9 @@ template <> struct MappingTraits<const InterfaceFile *> {
       auto Ctx = reinterpret_cast<TextAPIContext *>(IO.getContext());
       assert(Ctx);
       TBDVersion = Ctx->FileKind >> 4;
-      Targets.insert(Targets.begin(), File->targets().begin(),
-                     File->targets().end());
+      for (auto &T : File->targets())
+        if (T.isValid())
+          Targets.push_back(T);
       InstallName = File->getInstallName();
       CurrentVersion = File->getCurrentVersion();
       CompatibilityVersion = File->getCompatibilityVersion();
@@ -789,7 +811,8 @@ template <> struct MappingTraits<const InterfaceFile *> {
       {
         std::map<std::string, TargetList> valueToTargetList;
         for (const auto &it : File->umbrellas())
-          valueToTargetList[it.second].emplace_back(it.first);
+          if (it.first.isValid())
+            valueToTargetList[it.second].emplace_back(it.first);
 
         for (const auto &it : valueToTargetList) {
           UmbrellaSection CurrentSection;
@@ -809,7 +832,11 @@ template <> struct MappingTraits<const InterfaceFile *> {
             std::set<TargetList> TargetSet;
             std::map<const Symbol *, TargetList> SymbolToTargetList;
             for (const auto *Symbol : Symbols) {
-              TargetList Targets(Symbol->targets());
+              TargetList Targets;
+              for (auto &T : Symbol->targets())
+                if (T.isValid())
+                  Targets.push_back(T);
+
               SymbolToTargetList[Symbol] = Targets;
               TargetSet.emplace(std::move(Targets));
             }
@@ -899,6 +926,9 @@ template <> struct MappingTraits<const InterfaceFile *> {
         const SymbolFlags Flag = InputFlag | SymbolFlags::Data;
 
         for (const auto &CurrentSection : CurrentSections) {
+          if (CurrentSection.Targets.empty())
+            continue;
+
           for (auto &sym : CurrentSection.Symbols)
             File->addSymbol(EncodeKind::GlobalSymbol, sym,
                             CurrentSection.Targets, Flag);
@@ -1019,6 +1049,9 @@ template <> struct MappingTraits<const InterfaceFile *> {
     IO.mapTag("!tapi-tbd", true);
     IO.mapRequired("tbd-version", Keys->TBDVersion);
     IO.mapRequired("targets", Keys->Targets);
+    if (!IO.outputting())
+      llvm::erase_if(Keys->Targets,
+                     [](const Target &T) { return !T.isValid(); });
     IO.mapOptional("uuids", EmptyUUID);
     IO.mapOptional("flags", Keys->Flags, TBDFlags::None);
     IO.mapRequired("install-name", Keys->InstallName);
@@ -1095,8 +1128,10 @@ Expected<FileType> TextAPIReader::canRead(MemoryBufferRef InputBuffer) {
 }
 
 Expected<std::unique_ptr<InterfaceFile>>
-TextAPIReader::get(MemoryBufferRef InputBuffer) {
+TextAPIReader::get(MemoryBufferRef InputBuffer, bool SkipUnknownTriples) {
   TextAPIContext Ctx;
+
+  Ctx.SkipUnknownTriples = SkipUnknownTriples;
   Ctx.Path = std::string(InputBuffer.getBufferIdentifier());
   if (auto FTOrErr = canRead(InputBuffer))
     Ctx.FileKind = *FTOrErr;

diff  --git a/llvm/lib/TextAPI/TextStubV5.cpp b/llvm/lib/TextAPI/TextStubV5.cpp
index de40d84f5e7a6..2fb74798bb72b 100644
--- a/llvm/lib/TextAPI/TextStubV5.cpp
+++ b/llvm/lib/TextAPI/TextStubV5.cpp
@@ -12,6 +12,7 @@
 #include "TextStubCommon.h"
 #include "llvm/ADT/StringSwitch.h"
 #include "llvm/Support/JSON.h"
+#include <optional>
 #include <utility>
 
 // clang-format off
@@ -279,6 +280,15 @@ Expected<FileType> getVersion(const Object *File) {
   return *VersionOrErr;
 }
 
+Expected<std::optional<MachO::Target>> parseTargetStr(StringRef Str) {
+  auto TargetOrErr = MachO::Target::create(Str);
+  if (!TargetOrErr)
+    return make_error<JSONStubError>(getParseErrorMsg(TBDKey::Target));
+  if (!TargetOrErr->isValid())
+    return std::nullopt;
+  return *TargetOrErr;
+}
+
 Expected<TargetList> getTargets(const Object *Section) {
   const auto *Targets = Section->getArray(Keys[TBDKey::Targets]);
   if (!Targets)
@@ -289,10 +299,12 @@ Expected<TargetList> getTargets(const Object *Section) {
     auto TargetStr = JSONTarget.getAsString();
     if (!TargetStr.has_value())
       return make_error<JSONStubError>(getParseErrorMsg(TBDKey::Target));
-    auto TargetOrErr = Target::create(TargetStr.value());
+    auto TargetOrErr = parseTargetStr(TargetStr.value());
     if (!TargetOrErr)
-      return make_error<JSONStubError>(getParseErrorMsg(TBDKey::Target));
-    IFTargets.push_back(*TargetOrErr);
+      return TargetOrErr.takeError();
+    if (!TargetOrErr->has_value())
+      continue;
+    IFTargets.push_back(**TargetOrErr);
   }
   return std::move(IFTargets);
 }
@@ -311,20 +323,22 @@ Expected<TargetList> getTargetsSection(const Object *Section) {
         getRequiredValue<StringRef>(TBDKey::Target, Obj, &Object::getString);
     if (!TargetStr)
       return make_error<JSONStubError>(getParseErrorMsg(TBDKey::Target));
-    auto TargetOrErr = Target::create(*TargetStr);
+    auto TargetOrErr = parseTargetStr(*TargetStr);
     if (!TargetOrErr)
-      return make_error<JSONStubError>(getParseErrorMsg(TBDKey::Target));
+      return TargetOrErr.takeError();
+    if (!TargetOrErr->has_value())
+      continue;
 
     auto VersionStr = Obj->getString(Keys[TBDKey::Deployment]);
     VersionTuple Version;
     if (VersionStr && Version.tryParse(*VersionStr))
       return make_error<JSONStubError>(getParseErrorMsg(TBDKey::Deployment));
-    TargetOrErr->MinDeployment = Version;
+    (*TargetOrErr)->MinDeployment = Version;
 
     // Convert to LLVM::Triple to accurately compute minOS + platform + arch
     // pairing.
     IFTargets.push_back(
-        MachO::Target(Triple(getTargetTripleName(*TargetOrErr))));
+        MachO::Target(Triple(getTargetTripleName(**TargetOrErr))));
   }
   return std::move(IFTargets);
 }
@@ -701,15 +715,24 @@ Expected<IFPtr> parseToInterfaceFile(const Object *File) {
   for (auto &[Path, Targets] : RPaths)
     for (auto Target : Targets)
       F->addRPath(Path, Target);
-  for (auto &[Targets, Symbols] : Exports)
+  for (auto &[Targets, Symbols] : Exports) {
+    if (Targets.empty())
+      continue;
     for (auto &Sym : Symbols)
       F->addSymbol(Sym.Kind, Sym.Name, Targets, Sym.Flags);
-  for (auto &[Targets, Symbols] : Reexports)
+  }
+  for (auto &[Targets, Symbols] : Reexports) {
+    if (Targets.empty())
+      continue;
     for (auto &Sym : Symbols)
       F->addSymbol(Sym.Kind, Sym.Name, Targets, Sym.Flags);
-  for (auto &[Targets, Symbols] : Undefineds)
+  }
+  for (auto &[Targets, Symbols] : Undefineds) {
+    if (Targets.empty())
+      continue;
     for (auto &Sym : Symbols)
       F->addSymbol(Sym.Kind, Sym.Name, Targets, Sym.Flags);
+  }
 
   return std::move(F);
 }

diff  --git a/llvm/unittests/TextAPI/TextStubHelpers.h b/llvm/unittests/TextAPI/TextStubHelpers.h
index 87ca7e1c0b4d4..74b6ec3fa3790 100644
--- a/llvm/unittests/TextAPI/TextStubHelpers.h
+++ b/llvm/unittests/TextAPI/TextStubHelpers.h
@@ -6,6 +6,7 @@
 //
 //===-----------------------------------------------------------------------===/
 
+#include "llvm/ADT/DenseSet.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/TextAPI/InterfaceFile.h"
 #include <string>

diff  --git a/llvm/unittests/TextAPI/TextStubV3Tests.cpp b/llvm/unittests/TextAPI/TextStubV3Tests.cpp
index 1da918f2df9e0..3c822b2188a41 100644
--- a/llvm/unittests/TextAPI/TextStubV3Tests.cpp
+++ b/llvm/unittests/TextAPI/TextStubV3Tests.cpp
@@ -937,4 +937,52 @@ TEST(TBDv3, InterfaceInequality) {
   }));
 }
 
+TEST(TBDv3, SkipUnknownArch) {
+  static const char TBDv3WithUnknownArch[] = "--- !tapi-tbd-v3\n"
+                                             "archs: [ arm64, foo ]\n"
+                                             "platform: ios\n"
+                                             "install-name: Test.dylib\n"
+                                             "exports:\n"
+                                             "  - archs: [ arm64 ]\n"
+                                             "    symbols: [ _knownSym ]\n"
+                                             "  - archs: [ foo ]\n"
+                                             "    symbols: [ _unknownSym ]\n"
+                                             "  - archs: [ arm64, foo ]\n"
+                                             "    symbols: [ _mixedSym ]\n"
+                                             "...\n";
+
+  Expected<TBDFile> Result =
+      TextAPIReader::get(MemoryBufferRef(TBDv3WithUnknownArch, "Test.tbd"),
+                         /*SkipUnknownTriples=*/true);
+  EXPECT_TRUE(!!Result);
+  TBDFile File = std::move(Result.get());
+
+  EXPECT_EQ(ArchitectureSet(AK_arm64), File->getArchitectures());
+
+  llvm::DenseSet<StringRef> SymbolNames;
+  for (const auto *Sym : File->exports())
+    SymbolNames.insert(Sym->getName());
+
+  EXPECT_TRUE(SymbolNames.count("_knownSym"));
+  EXPECT_TRUE(SymbolNames.count("_mixedSym"));
+  EXPECT_FALSE(SymbolNames.count("_unknownSym"));
+}
+
+TEST(TBDv3, SkipAllUnknownArchs) {
+  static const char TBDv3AllUnknown[] = "--- !tapi-tbd-v3\n"
+                                        "archs: [ foo, bar ]\n"
+                                        "platform: ios\n"
+                                        "install-name: Test.dylib\n"
+                                        "...\n";
+
+  Expected<TBDFile> Result =
+      TextAPIReader::get(MemoryBufferRef(TBDv3AllUnknown, "Test.tbd"),
+                         /*SkipUnknownTriples=*/true);
+  EXPECT_TRUE(!!Result);
+  TBDFile File = std::move(Result.get());
+
+  EXPECT_TRUE(File->getArchitectures().empty());
+  EXPECT_EQ(File->symbolsCount(), 0U);
+}
+
 } // namespace TBDv3

diff  --git a/llvm/unittests/TextAPI/TextStubV4Tests.cpp b/llvm/unittests/TextAPI/TextStubV4Tests.cpp
index 1a10c9119a43c..3f9d8d9b7deaa 100644
--- a/llvm/unittests/TextAPI/TextStubV4Tests.cpp
+++ b/llvm/unittests/TextAPI/TextStubV4Tests.cpp
@@ -896,7 +896,7 @@ TEST(TBDv4, InvalidArchitecture) {
   EXPECT_FALSE(!!Result);
   std::string ErrorMessage = toString(Result.takeError());
   EXPECT_EQ("malformed file\nTest.tbd:3:12: error: unknown "
-            "architecture\ntargets: [ foo-macos ]\n"
+            "target\ntargets: [ foo-macos ]\n"
             "           ^~~~~~~~~~\n",
             ErrorMessage);
 }
@@ -912,7 +912,7 @@ TEST(TBDv4, InvalidPlatform) {
       TextAPIReader::get(MemoryBufferRef(TBDv4FInvalidPlatform, "Test.tbd"));
   EXPECT_FALSE(!!Result);
   std::string ErrorMessage = toString(Result.takeError());
-  EXPECT_EQ("malformed file\nTest.tbd:3:12: error: unknown platform\ntargets: "
+  EXPECT_EQ("malformed file\nTest.tbd:3:12: error: unknown target\ntargets: "
             "[ x86_64-maos ]\n"
             "           ^~~~~~~~~~~~\n",
             ErrorMessage);
@@ -1175,4 +1175,174 @@ TEST(TBDv4, InterfaceInequality) {
   }));
 }
 
+TEST(TBDv4, SkipUnknownArch) {
+  static const char TBDv4WithUnknownArch[] =
+      "--- !tapi-tbd\n"
+      "tbd-version: 4\n"
+      "targets: [ x86_64-macos, foo-macos ]\n"
+      "install-name: Test.dylib\n"
+      "exports:\n"
+      "  - targets: [ x86_64-macos ]\n"
+      "    symbols: [ _knownSym ]\n"
+      "  - targets: [ foo-bar ]\n"
+      "    symbols: [ _unknownSym ]\n"
+      "  - targets: [ x86_64-macos, foo-macos ]\n"
+      "    symbols: [ _mixedSym ]\n"
+      "...\n";
+
+  Expected<TBDFile> Result =
+      TextAPIReader::get(MemoryBufferRef(TBDv4WithUnknownArch, "Test.tbd"),
+                         /*SkipUnknownTriples=*/true);
+  EXPECT_TRUE(!!Result);
+  TBDFile File = std::move(Result.get());
+
+  EXPECT_EQ(1U, llvm::size(File->targets()));
+  EXPECT_EQ(Target(AK_x86_64, PLATFORM_MACOS), *File->targets().begin());
+
+  llvm::DenseSet<StringRef> SymbolNames;
+  for (const auto *Sym : File->exports())
+    SymbolNames.insert(Sym->getName());
+
+  EXPECT_TRUE(SymbolNames.count("_knownSym"));
+  EXPECT_TRUE(SymbolNames.count("_mixedSym"));
+  EXPECT_FALSE(SymbolNames.count("_unknownSym"));
+}
+
+TEST(TBDv4, SkipUnknownPlatform) {
+  static const char TBDv4WithUnknownPlatform[] =
+      "--- !tapi-tbd\n"
+      "tbd-version: 4\n"
+      "targets: [ x86_64-macos, x86_64-windows ]\n"
+      "install-name: Test.dylib\n"
+      "exports:\n"
+      "  - targets: [ x86_64-macos ]\n"
+      "    symbols: [ _knownSym ]\n"
+      "  - targets: [ x86_64-unknownos ]\n"
+      "    symbols: [ _unknownSym ]\n"
+      "...\n";
+
+  Expected<TBDFile> Result =
+      TextAPIReader::get(MemoryBufferRef(TBDv4WithUnknownPlatform, "Test.tbd"),
+                         /*SkipUnknownTriples=*/true);
+  EXPECT_TRUE(!!Result);
+  TBDFile File = std::move(Result.get());
+
+  EXPECT_EQ(1U, llvm::size(File->targets()));
+  EXPECT_EQ(Target(AK_x86_64, PLATFORM_MACOS), *File->targets().begin());
+
+  DenseSet<StringRef> SymbolNames;
+  for (const auto *Sym : File->exports())
+    SymbolNames.insert(Sym->getName());
+
+  EXPECT_TRUE(SymbolNames.count("_knownSym"));
+  EXPECT_FALSE(SymbolNames.count("_unknownSym"));
+}
+
+TEST(TBDv4, SkipAllUnknownTargets) {
+  static const char TBDv4AllUnknown[] = "--- !tapi-tbd\n"
+                                        "tbd-version: 4\n"
+                                        "targets: [ foo-macos, bar-ios ]\n"
+                                        "install-name: Test.dylib\n"
+                                        "...\n";
+
+  Expected<TBDFile> Result =
+      TextAPIReader::get(MemoryBufferRef(TBDv4AllUnknown, "Test.tbd"),
+                         /*SkipUnknownTriples=*/true);
+  EXPECT_TRUE(!!Result);
+  TBDFile File = std::move(Result.get());
+
+  // All targets should be filtered out.
+  EXPECT_EQ(0U, llvm::size(File->targets()));
+}
+
+TEST(TBDv4, SkipUnknownWithLoadCommands) {
+  static const char TBDv4WithMetadata[] =
+      "--- !tapi-tbd\n"
+      "tbd-version: 4\n"
+      "targets: [ x86_64-macos, foo-macos ]\n"
+      "install-name: Test.dylib\n"
+      "parent-umbrella:\n"
+      "  - targets: [ x86_64-macos, foo-macos ]\n"
+      "    umbrella: System\n"
+      "allowable-clients:\n"
+      "  - targets: [ foo-macos ]\n"
+      "    clients: [ UnknownClient ]\n"
+      "  - targets: [ x86_64-macos ]\n"
+      "    clients: [ KnownClient ]\n"
+      "...\n";
+
+  Expected<TBDFile> Result =
+      TextAPIReader::get(MemoryBufferRef(TBDv4WithMetadata, "Test.tbd"),
+                         /*SkipUnknownTriples=*/true);
+  EXPECT_TRUE(!!Result);
+  TBDFile File = std::move(Result.get());
+
+  EXPECT_EQ(1U, llvm::size(File->targets()));
+
+  auto &Umbrellas = File->umbrellas();
+  EXPECT_EQ(1U, std::distance(Umbrellas.begin(), Umbrellas.end()));
+  EXPECT_EQ(Target(AK_x86_64, PLATFORM_MACOS), Umbrellas.begin()->first);
+
+  auto &Clients = File->allowableClients();
+  EXPECT_EQ(1U, Clients.size());
+  EXPECT_EQ("KnownClient", Clients.front().getInstallName());
+}
+
+TEST(TBDv4, SkipUnknownInFullFile) {
+  static const char TBDv4FullFile[] =
+      "--- !tapi-tbd\n"
+      "tbd-version: 4\n"
+      "targets: [ arm64-macos, x86_64-macos, arm64-linux ]\n"
+      "install-name: Test.dylib\n"
+      "current-version: 1.2.3\n"
+      "compatibility-version: 1.2\n"
+      "parent-umbrella:\n"
+      "  - targets: [ arm64-macos, x86_64-macos, arm64-linux ]\n"
+      "    umbrella: System\n"
+      "allowable-clients:\n"
+      "  - targets: [ arm64-macos, x86_64-macos, arm64-linux ]\n"
+      "    clients: [ ClientA ]\n"
+      "reexported-libraries:\n"
+      "  - targets: [ arm64-macos, x86_64-macos, arm64-linux ]\n"
+      "    libraries: [ /System/Library/Frameworks/A.framework/A ]\n"
+      "exports:\n"
+      "  - targets: [ arm64-macos, x86_64-macos, arm64-linux ]\n"
+      "    symbols: [ _sym ]\n"
+      "...\n";
+  Expected<TBDFile> Result = TextAPIReader::get(
+      MemoryBufferRef(TBDv4FullFile, "Test.tbd"), /*SkipUnknownTriples=*/true);
+  EXPECT_TRUE(!!Result);
+  TBDFile File = std::move(Result.get());
+
+  TargetList ExpectedTargets = {
+      Target(AK_x86_64, PLATFORM_MACOS),
+      Target(AK_arm64, PLATFORM_MACOS),
+  };
+  EXPECT_EQ(2U, llvm::size(File->targets()));
+  for (const auto &T : File->targets())
+    EXPECT_TRUE(llvm::is_contained(ExpectedTargets, T));
+
+  auto &Umbrellas = File->umbrellas();
+  EXPECT_EQ(2U, std::distance(Umbrellas.begin(), Umbrellas.end()));
+  for (const auto &U : Umbrellas)
+    EXPECT_TRUE(llvm::is_contained(ExpectedTargets, U.first));
+
+  InterfaceFileRef ExpectedClient("ClientA", ExpectedTargets);
+  EXPECT_EQ(1U, File->allowableClients().size());
+  EXPECT_EQ(ExpectedClient, File->allowableClients().front());
+
+  InterfaceFileRef ExpectedReexport("/System/Library/Frameworks/A.framework/A",
+                                    ExpectedTargets);
+  EXPECT_EQ(1U, File->reexportedLibraries().size());
+  EXPECT_EQ(ExpectedReexport, File->reexportedLibraries().front());
+
+  auto Exports = File->exports();
+  EXPECT_EQ(1, std::distance(Exports.begin(), Exports.end()));
+  const Symbol *Sym = *Exports.begin();
+  EXPECT_EQ("_sym", Sym->getName());
+  EXPECT_EQ(2U, llvm::size(Sym->targets()));
+  for (const auto &T : Sym->targets())
+    EXPECT_TRUE(llvm::is_contained(ExpectedTargets, T));
+}
+
 } // end namespace TBDv4

diff  --git a/llvm/unittests/TextAPI/TextStubV5Tests.cpp b/llvm/unittests/TextAPI/TextStubV5Tests.cpp
index f6689f7524a72..a5525fdb207ed 100644
--- a/llvm/unittests/TextAPI/TextStubV5Tests.cpp
+++ b/llvm/unittests/TextAPI/TextStubV5Tests.cpp
@@ -2478,4 +2478,164 @@ TEST(TBDv5, InlineIF) {
   EXPECT_TRUE(
       std::equal(Exports.begin(), Exports.end(), std::begin(ExpectedExports)));
 }
+
+TEST(TBDv5, SkipUnknownArch) {
+  static const char TBDv5WithUnknownArch[] = R"({
+"tapi_tbd_version": 5,
+"main_library": {
+  "target_info": [
+    { "target": "x86_64-macos" },
+    { "target": "foo-macos" }
+  ],
+  "install_names": [
+    { "name": "Test.dylib" }
+  ],
+  "exported_symbols": [
+    {
+      "targets": [ "x86_64-macos" ],
+      "data": { "global": [ "_knownSym" ] }
+    },
+    {
+      "targets": [ "foo-macos" ],
+      "data": { "global": [ "_unknownSym" ] }
+    },
+    {
+      "targets": [ "x86_64-macos", "foo-macos" ],
+      "data": { "global": [ "_mixedSym" ] }
+    }
+  ]
+}})";
+
+  Expected<TBDFile> Result =
+      TextAPIReader::get(MemoryBufferRef(TBDv5WithUnknownArch, "Test.tbd"),
+                         /*SkipUnknownTriples=*/true);
+  EXPECT_TRUE(!!Result);
+  TBDFile File = std::move(Result.get());
+
+  EXPECT_EQ(1U, llvm::size(File->targets()));
+  EXPECT_EQ(Target(AK_x86_64, PLATFORM_MACOS), *File->targets().begin());
+
+  llvm::DenseSet<StringRef> SymbolNames;
+  for (const auto *Sym : File->exports())
+    SymbolNames.insert(Sym->getName());
+
+  EXPECT_TRUE(SymbolNames.count("_knownSym"));
+  EXPECT_TRUE(SymbolNames.count("_mixedSym"));
+  EXPECT_FALSE(SymbolNames.count("_unknownSym"));
+}
+
+TEST(TBDv5, SkipUnknownPlatform) {
+  static const char TBDv5WithUnknownPlatform[] = R"({
+"tapi_tbd_version": 5,
+"main_library": {
+  "target_info": [
+    { "target": "x86_64-macos" },
+    { "target": "x86_64-unknownos" }
+  ],
+  "install_names": [
+    { "name": "Test.dylib" }
+  ],
+  "exported_symbols": [
+    {
+      "targets": [ "x86_64-macos" ],
+      "data": { "global": [ "_knownSym" ] }
+    },
+    {
+      "targets": [ "x86_64-unknownos" ],
+      "data": { "global": [ "_unknownSym" ] }
+    }
+  ]
+}})";
+
+  Expected<TBDFile> Result =
+      TextAPIReader::get(MemoryBufferRef(TBDv5WithUnknownPlatform, "Test.tbd"),
+                         /*SkipUnknownTriples=*/true);
+  EXPECT_TRUE(!!Result);
+  TBDFile File = std::move(Result.get());
+
+  EXPECT_EQ(1U, llvm::size(File->targets()));
+  EXPECT_EQ(Target(AK_x86_64, PLATFORM_MACOS), *File->targets().begin());
+
+  llvm::DenseSet<StringRef> SymbolNames;
+  for (const auto *Sym : File->exports())
+    SymbolNames.insert(Sym->getName());
+
+  EXPECT_TRUE(SymbolNames.count("_knownSym"));
+  EXPECT_FALSE(SymbolNames.count("_unknownSym"));
+}
+
+TEST(TBDv5, SkipAllUnknownTargets) {
+  static const char TBDv5AllUnknown[] = R"({
+"tapi_tbd_version": 5,
+"main_library": {
+  "target_info": [
+    { "target": "foo-macos" },
+    { "target": "bar-ios" }
+  ],
+  "install_names": [
+    { "name": "Test.dylib" }
+  ]
+}})";
+
+  Expected<TBDFile> Result =
+      TextAPIReader::get(MemoryBufferRef(TBDv5AllUnknown, "Test.tbd"),
+                         /*SkipUnknownTriples=*/true);
+  EXPECT_TRUE(!!Result);
+  TBDFile File = std::move(Result.get());
+
+  EXPECT_EQ(0U, llvm::size(File->targets()));
+}
+
+TEST(TBDv5, SkipUnknownWithInlinedLibs) {
+  static const char TBDv5WithInlined[] = R"({
+"tapi_tbd_version": 5,
+"main_library": {
+  "target_info": [
+    { "target": "x86_64-macos" },
+    { "target": "foo-macos" }
+  ],
+  "install_names": [
+    { "name": "/S/L/F/Umbrella.framework/Umbrella" }
+  ],
+  "reexported_libraries": [
+    { "names": [ "/S/L/F/A.framework/A" ] }
+  ]
+},
+"libraries": [
+  {
+    "target_info": [
+      { "target": "x86_64-macos" }
+    ],
+    "install_names": [
+      { "name": "/S/L/F/A.framework/A" }
+    ],
+    "exported_symbols": [
+      {
+        "data": { "global": [ "_inlinedSym" ] }
+      }
+    ]
+  }
+]
+})";
+
+  Expected<TBDFile> Result =
+      TextAPIReader::get(MemoryBufferRef(TBDv5WithInlined, "Test.tbd"),
+                         /*SkipUnknownTriples=*/true);
+  EXPECT_TRUE(!!Result);
+  TBDFile File = std::move(Result.get());
+
+  EXPECT_EQ(1U, llvm::size(File->targets()));
+  EXPECT_EQ(Target(AK_x86_64, PLATFORM_MACOS), *File->targets().begin());
+
+  EXPECT_EQ(1U, File->documents().size());
+  TBDReexportFile Document = File->documents().front();
+  EXPECT_EQ(1U, llvm::size(Document->targets()));
+  EXPECT_EQ(Target(AK_x86_64, PLATFORM_MACOS), *Document->targets().begin());
+
+  llvm::DenseSet<StringRef> SymbolNames;
+  for (const auto *Sym : Document->exports())
+    SymbolNames.insert(Sym->getName());
+  EXPECT_TRUE(SymbolNames.count("_inlinedSym"));
+}
+
 } // end namespace TBDv5


        


More information about the llvm-commits mailing list