[llvm] b861b12 - [TextAPI] Implement TBDv5 Reader
Cyndy Ishida via llvm-commits
llvm-commits at lists.llvm.org
Fri Feb 17 16:05:57 PST 2023
Author: Cyndy Ishida
Date: 2023-02-17T16:03:01-08:00
New Revision: b861b1225380175a5a724e2a677754f5f74e5b0d
URL: https://github.com/llvm/llvm-project/commit/b861b1225380175a5a724e2a677754f5f74e5b0d
DIFF: https://github.com/llvm/llvm-project/commit/b861b1225380175a5a724e2a677754f5f74e5b0d.diff
LOG: [TextAPI] Implement TBDv5 Reader
[TextAPI] Implement TBDv5 Reader
Introduce initial reader for TBDv5 which is in JSON. This captures all
the currently understood fields within the internal structure
`InterfaceFile`.
New fields & follow up tests will be followed up in future PRs.
Reviewed By: pete
Differential Revision: https://reviews.llvm.org/D144156
Added:
llvm/lib/TextAPI/TextStubV5.cpp
llvm/unittests/TextAPI/TextStubV5Tests.cpp
Modified:
lld/test/MachO/Inputs/libStubLink.tbd
lld/test/MachO/invalid/invalid-stub.s
lld/test/MachO/tapi-link.s
llvm/include/llvm/TextAPI/InterfaceFile.h
llvm/lib/TextAPI/CMakeLists.txt
llvm/lib/TextAPI/TextStub.cpp
llvm/lib/TextAPI/TextStubCommon.cpp
llvm/lib/TextAPI/TextStubCommon.h
llvm/test/Object/Inputs/tapi-v4-watchos.tbd
llvm/test/tools/llvm-tapi-diff/Inputs/v4A.tbd
llvm/test/tools/llvm-tapi-diff/Inputs/v4B.tbd
llvm/test/tools/llvm-tapi-diff/Inputs/v4C.tbd
llvm/test/tools/llvm-tapi-diff/Inputs/v4D.tbd
llvm/test/tools/llvm-tapi-diff/Inputs/v4E.tbd
llvm/unittests/TextAPI/CMakeLists.txt
llvm/unittests/TextAPI/TextStubHelpers.h
llvm/unittests/TextAPI/TextStubV4Tests.cpp
Removed:
################################################################################
diff --git a/lld/test/MachO/Inputs/libStubLink.tbd b/lld/test/MachO/Inputs/libStubLink.tbd
index 6e6237edbc8a2..eaf216d0a1f3f 100644
--- a/lld/test/MachO/Inputs/libStubLink.tbd
+++ b/lld/test/MachO/Inputs/libStubLink.tbd
@@ -22,3 +22,4 @@ current-version: 1.0.0
exports:
- targets: [ arm64-ios-simulator ]
symbols: [ _arm64_sim_only ]
+...
diff --git a/lld/test/MachO/invalid/invalid-stub.s b/lld/test/MachO/invalid/invalid-stub.s
index 997594918cd53..a80dfa6f8f15c 100644
--- a/lld/test/MachO/invalid/invalid-stub.s
+++ b/lld/test/MachO/invalid/invalid-stub.s
@@ -7,9 +7,8 @@
# RUN: not %lld -L%t -linvalidYAML %t/test.o -o %t/test 2>&1 | FileCheck %s -DDIR=%t
# RUN: not %lld -F%t -framework invalidYAML %t/test.o -o %t/test 2>&1 | FileCheck %s -DDIR=%t --check-prefix=CHECK-FRAMEWORK
-# CHECK: could not load TAPI file at [[DIR]]{{[\\/]}}libinvalidYAML.tbd: malformed file
-# CHECK-FRAMEWORK: could not load TAPI file at [[DIR]]{{[\\/]}}invalidYAML.framework{{[\\/]}}invalidYAML.tbd: malformed file
-
+# CHECK: could not load TAPI file at [[DIR]]{{[\\/]}}libinvalidYAML.tbd: unsupported file type
+# CHECK-FRAMEWORK: could not load TAPI file at [[DIR]]{{[\\/]}}invalidYAML.framework{{[\\/]}}invalidYAML.tbd: unsupported file type
.globl _main
_main:
ret
diff --git a/lld/test/MachO/tapi-link.s b/lld/test/MachO/tapi-link.s
index b2aa2f208281e..af205b6c24e99 100644
--- a/lld/test/MachO/tapi-link.s
+++ b/lld/test/MachO/tapi-link.s
@@ -121,7 +121,6 @@ exports:
re-exports: [ 'libNested.dylib' ]
...
-## This tests that weak and thread-local symbols are imported as such.
#--- libTlvWeak.tbd
--- !tapi-tbd
tbd-version: 4
@@ -131,8 +130,8 @@ uuids:
value: 00000000-0000-0000-0000-000000000000
install-name: '/usr/lib/libTlvWeak.dylib'
current-version: 0001.001.1
-exports:
+exports: # Validate weak & thread-local symbols
- targets: [ x86_64-macos ]
weak-symbols: [ _weak ]
thread-local-symbols: [ _tlv ]
----
+...
diff --git a/llvm/include/llvm/TextAPI/InterfaceFile.h b/llvm/include/llvm/TextAPI/InterfaceFile.h
index 5f07397adacaa..cb8ec4b8f76d6 100644
--- a/llvm/include/llvm/TextAPI/InterfaceFile.h
+++ b/llvm/include/llvm/TextAPI/InterfaceFile.h
@@ -66,6 +66,9 @@ enum FileType : unsigned {
/// Text-based stub file (.tbd) version 4.0
TBD_V4 = 1U << 3,
+ /// Text-based stub file (.tbd) version 5.0
+ TBD_V5 = 1U << 4,
+
All = ~0U,
LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/All),
diff --git a/llvm/lib/TextAPI/CMakeLists.txt b/llvm/lib/TextAPI/CMakeLists.txt
index c7e9e1009ab22..1714ce33ef205 100644
--- a/llvm/lib/TextAPI/CMakeLists.txt
+++ b/llvm/lib/TextAPI/CMakeLists.txt
@@ -2,6 +2,7 @@ add_llvm_component_library(LLVMTextAPI
Architecture.cpp
ArchitectureSet.cpp
InterfaceFile.cpp
+ TextStubV5.cpp
PackedVersion.cpp
Platform.cpp
Symbol.cpp
diff --git a/llvm/lib/TextAPI/TextStub.cpp b/llvm/lib/TextAPI/TextStub.cpp
index ff93e43356f73..73cb61472bff3 100644
--- a/llvm/lib/TextAPI/TextStub.cpp
+++ b/llvm/lib/TextAPI/TextStub.cpp
@@ -258,16 +258,6 @@ struct UUIDv4 {
UUIDv4(const Target &TargetID, const std::string &Value)
: TargetID(TargetID), Value(Value) {}
};
-
-// clang-format off
-enum TBDFlags : unsigned {
- None = 0U,
- FlatNamespace = 1U << 0,
- NotApplicationExtensionSafe = 1U << 1,
- InstallAPI = 1U << 2,
- LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/InstallAPI),
-};
-// clang-format on
} // end anonymous namespace.
LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(Architecture)
@@ -1105,10 +1095,49 @@ static void DiagHandler(const SMDiagnostic &Diag, void *Context) {
File->ErrorMessage = ("malformed file\n" + Message).str();
}
+namespace {
+
+Expected<FileType> canReadFileType(MemoryBufferRef InputBuffer) {
+ auto TAPIFile = InputBuffer.getBuffer().trim();
+ if (TAPIFile.startswith("{") && TAPIFile.endswith("}"))
+ return FileType::TBD_V5;
+
+ if (!TAPIFile.endswith("..."))
+ return createStringError(std::errc::not_supported, "unsupported file type");
+
+ if (TAPIFile.startswith("--- !tapi-tbd\n"))
+ return FileType::TBD_V4;
+
+ if (TAPIFile.startswith("--- !tapi-tbd-v3\n"))
+ return FileType::TBD_V3;
+
+ if (TAPIFile.startswith("--- !tapi-tbd-v2\n"))
+ return FileType::TBD_V2;
+
+ if (TAPIFile.startswith("--- !tapi-tbd-v1\n") ||
+ TAPIFile.startswith("---\narchs:"))
+ return FileType::TBD_V1;
+
+ return createStringError(std::errc::not_supported, "unsupported file type");
+}
+} // namespace
+
Expected<std::unique_ptr<InterfaceFile>>
TextAPIReader::get(MemoryBufferRef InputBuffer) {
TextAPIContext Ctx;
Ctx.Path = std::string(InputBuffer.getBufferIdentifier());
+ if (auto FTOrErr = canReadFileType(InputBuffer))
+ Ctx.FileKind = *FTOrErr;
+ else
+ return FTOrErr.takeError();
+
+ // Handle JSON Format.
+ if (Ctx.FileKind >= FileType::TBD_V5) {
+ auto FileOrErr = getInterfaceFileFromJSON(InputBuffer.getBuffer());
+ if (!FileOrErr)
+ return FileOrErr.takeError();
+ return std::move(*FileOrErr);
+ }
yaml::Input YAMLIn(InputBuffer.getBuffer(), &Ctx, DiagHandler, &Ctx);
// Fill vector with interface file objects created by parsing the YAML file.
diff --git a/llvm/lib/TextAPI/TextStubCommon.cpp b/llvm/lib/TextAPI/TextStubCommon.cpp
index 01a90078e1506..8959af4bfa09f 100644
--- a/llvm/lib/TextAPI/TextStubCommon.cpp
+++ b/llvm/lib/TextAPI/TextStubCommon.cpp
@@ -6,7 +6,7 @@
//
//===----------------------------------------------------------------------===//
//
-// Implememts common Text Stub YAML mappings.
+// Implements common Text Stub YAML mappings.
//
//===----------------------------------------------------------------------===//
diff --git a/llvm/lib/TextAPI/TextStubCommon.h b/llvm/lib/TextAPI/TextStubCommon.h
index aac27221b5fff..51b423137f4e9 100644
--- a/llvm/lib/TextAPI/TextStubCommon.h
+++ b/llvm/lib/TextAPI/TextStubCommon.h
@@ -22,6 +22,16 @@
using UUID = std::pair<llvm::MachO::Target, std::string>;
+// clang-format off
+enum TBDFlags : unsigned {
+ None = 0U,
+ FlatNamespace = 1U << 0,
+ NotApplicationExtensionSafe = 1U << 1,
+ InstallAPI = 1U << 2,
+ LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/InstallAPI),
+};
+// clang-format on
+
LLVM_YAML_STRONG_TYPEDEF(llvm::StringRef, FlowStringRef)
LLVM_YAML_STRONG_TYPEDEF(uint8_t, SwiftVersion)
LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(UUID)
@@ -30,9 +40,13 @@ LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FlowStringRef)
namespace llvm {
namespace MachO {
- class ArchitectureSet;
- class PackedVersion;
-}
+class ArchitectureSet;
+class PackedVersion;
+
+Expected<std::unique_ptr<InterfaceFile>>
+getInterfaceFileFromJSON(StringRef JSON);
+} // namespace MachO
+
namespace yaml {
template <> struct ScalarTraits<FlowStringRef> {
diff --git a/llvm/lib/TextAPI/TextStubV5.cpp b/llvm/lib/TextAPI/TextStubV5.cpp
new file mode 100644
index 0000000000000..431d285b3b2e5
--- /dev/null
+++ b/llvm/lib/TextAPI/TextStubV5.cpp
@@ -0,0 +1,700 @@
+//===- TextStubV5.cpp -----------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Implements Text Stub JSON mappings.
+//
+//===----------------------------------------------------------------------===//
+#include "TextStubCommon.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/JSON.h"
+
+// clang-format off
+/*
+
+JSON Format specification.
+
+All library level keys, accept target values and are defaulted if not specified.
+
+{
+"tapi_tbd_version": 5, # Required: TBD version for all documents in file
+"main_library": { # Required: top level library
+ "target_info": [ # Required: target information
+ {
+ "target": "x86_64-macos",
+ "min_deployment": "10.14" # Required: minimum OS deployment version
+ },
+ {
+ "target": "arm64-macos",
+ "min_deployment": "10.14"
+ },
+ {
+ "target": "arm64-maccatalyst",
+ "min_deployment": "12.1"
+ }],
+ "flags":[{"attributes": ["flat_namespace"]}], # Optional:
+ "install_names":[{"name":"/S/L/F/Foo.fwk/Foo"}], # Required: library install name
+ "current_versions":[{"version": "1.2"}], # Optional: defaults to 1
+ "compatibility_versions":[{ "version": "1.1"}], # Optional: defaults to 1
+ "rpaths": [ # Optional:
+ {
+ "targets": ["x86_64-macos"], # Optional: defaults to targets in `target-info`
+ "paths": ["@executable_path/.../Frameworks"]
+ }],
+ "parent_umbrellas": [{"umbrella": "System"}],
+ "allowable_clients": [{"clients": ["ClientA"]}],
+ "reexported_libraries": [{"names": ["/u/l/l/foo.dylib"]}],
+ "exported_symbols": [{ # List of export symbols section
+ "targets": ["x86_64-macos", "arm64-macos"], # Optional: defaults to targets in `target-info`
+ "text": { # List of Text segment symbols
+ "global": [ "_func" ],
+ "weak": [],
+ "thread_local": []
+ },
+ "data": { ... }, # List of Data segment symbols
+ }],
+ "reexported_symbols": [{ ... }], # List of reexported symbols section
+ "undefined_symbols": [{ ... }] # List of undefined symbols section
+},
+"libraries": [ # Optional: Array of inlined libraries
+ {...}, {...}, {...}
+]
+}
+*/
+// clang-format on
+
+using namespace llvm;
+using namespace llvm::json;
+using namespace llvm::MachO;
+
+struct JSONSymbol {
+ SymbolKind Kind;
+ std::string Name;
+ SymbolFlags Flags;
+};
+
+using AttrToTargets = std::map<std::string, TargetList>;
+using TargetsToSymbols =
+ SmallVector<std::pair<TargetList, std::vector<JSONSymbol>>>;
+
+enum TBDKey : size_t {
+ TBDVersion = 0U,
+ MainLibrary,
+ Documents,
+ TargetInfo,
+ Targets,
+ Target,
+ Deployment,
+ Flags,
+ Attributes,
+ InstallName,
+ CurrentVersion,
+ CompatibilityVersion,
+ Version,
+ SwiftABI,
+ ABI,
+ ParentUmbrella,
+ Umbrella,
+ AllowableClients,
+ Clients,
+ ReexportLibs,
+ Names,
+ Name,
+ Exports,
+ Reexports,
+ Undefineds,
+ Data,
+ Text,
+ Weak,
+ ThreadLocal,
+ Globals,
+ ObjCClass,
+ ObjCEHType,
+ ObjCIvar,
+};
+
+std::array<StringRef, 64> Keys = {
+ "tapi_tbd_version",
+ "main_library",
+ "libraries",
+ "target_info",
+ "targets",
+ "target",
+ "min_deployment",
+ "flags",
+ "attributes",
+ "install_names",
+ "current_versions",
+ "compatibility_versions",
+ "version",
+ "swift_abi",
+ "abi",
+ "parent_umbrellas",
+ "umbrella",
+ "allowable_clients",
+ "clients",
+ "reexported_libraries",
+ "names",
+ "name",
+ "exported_symbols",
+ "reexported_symbols",
+ "undefined_symbols",
+ "data",
+ "text",
+ "weak",
+ "thread_local",
+ "global",
+ "objc_class",
+ "objc_eh_type",
+ "objc_ivar",
+};
+
+static llvm::SmallString<128> getParseErrorMsg(TBDKey Key) {
+ return {"invalid ", Keys[Key], " section"};
+}
+
+class JSONStubError : public llvm::ErrorInfo<llvm::json::ParseError> {
+public:
+ JSONStubError(Twine ErrMsg) : Message(ErrMsg.str()) {}
+
+ void log(llvm::raw_ostream &OS) const override { OS << Message << "\n"; }
+ std::error_code convertToErrorCode() const override {
+ return llvm::inconvertibleErrorCode();
+ }
+
+private:
+ std::string Message;
+};
+
+template <typename JsonT, typename StubT = JsonT>
+Expected<StubT> getRequiredValue(
+ TBDKey Key, const Object *Obj,
+ std::function<std::optional<JsonT>(const Object *, StringRef)> GetValue,
+ std::function<std::optional<StubT>(JsonT)> Validate = nullptr) {
+ std::optional<JsonT> Val = GetValue(Obj, Keys[Key]);
+ if (!Val)
+ return make_error<JSONStubError>(getParseErrorMsg(Key));
+
+ if (Validate == nullptr)
+ return static_cast<StubT>(*Val);
+
+ std::optional<StubT> Result = Validate(*Val);
+ if (!Result.has_value())
+ return make_error<JSONStubError>(getParseErrorMsg(Key));
+ return Result.value();
+}
+
+template <typename JsonT, typename StubT = JsonT>
+Expected<StubT> getRequiredValue(
+ TBDKey Key, const Object *Obj,
+ std::function<std::optional<JsonT>(const Object *, StringRef)> GetValue,
+ StubT DefaultValue, std::function<std::optional<StubT>(JsonT)> Validate) {
+ std::optional<JsonT> Val = GetValue(Obj, Keys[Key]);
+ if (!Val)
+ return DefaultValue;
+
+ std::optional<StubT> Result;
+ Result = Validate(*Val);
+ if (!Result.has_value())
+ return make_error<JSONStubError>(getParseErrorMsg(Key));
+ return Result.value();
+}
+
+Error collectFromArray(TBDKey Key, const Object *Obj,
+ std::function<void(StringRef)> Append,
+ bool IsRequired = false) {
+ const auto *Values = Obj->getArray(Keys[Key]);
+ if (!Values) {
+ if (IsRequired)
+ return make_error<JSONStubError>(getParseErrorMsg(Key));
+ return Error::success();
+ }
+
+ for (Value Val : *Values) {
+ auto ValStr = Val.getAsString();
+ if (!ValStr.has_value())
+ return make_error<JSONStubError>(getParseErrorMsg(Key));
+ Append(ValStr.value());
+ }
+
+ return Error::success();
+}
+
+namespace StubParser {
+
+Expected<FileType> getVersion(const Object *File) {
+ auto VersionOrErr = getRequiredValue<int64_t, FileType>(
+ TBDKey::TBDVersion, File, &Object::getInteger,
+ [](int64_t Val) -> std::optional<FileType> {
+ unsigned Result = Val;
+ if (Result != 5)
+ return std::nullopt;
+ return FileType::TBD_V5;
+ });
+
+ if (!VersionOrErr)
+ return VersionOrErr.takeError();
+ return *VersionOrErr;
+}
+
+Expected<TargetList> getTargets(const Object *Section) {
+ const auto *Targets = Section->getArray(Keys[TBDKey::Targets]);
+ if (!Targets)
+ return make_error<JSONStubError>(getParseErrorMsg(TBDKey::Targets));
+
+ TargetList IFTargets;
+ for (Value JSONTarget : *Targets) {
+ auto TargetStr = JSONTarget.getAsString();
+ if (!TargetStr.has_value())
+ return make_error<JSONStubError>(getParseErrorMsg(TBDKey::Target));
+ auto TargetOrErr = Target::create(TargetStr.value());
+ if (!TargetOrErr)
+ return make_error<JSONStubError>(getParseErrorMsg(TBDKey::Target));
+ IFTargets.push_back(*TargetOrErr);
+ }
+ return IFTargets;
+}
+
+Expected<TargetList> getTargetsSection(const Object *Section) {
+ const Array *Targets = Section->getArray(Keys[TBDKey::TargetInfo]);
+ if (!Targets)
+ return make_error<JSONStubError>(getParseErrorMsg(TBDKey::Targets));
+
+ TargetList IFTargets;
+ for (const Value &JSONTarget : *Targets) {
+ const auto *Obj = JSONTarget.getAsObject();
+ if (!Obj)
+ return make_error<JSONStubError>(getParseErrorMsg(TBDKey::Target));
+ auto TargetStr =
+ getRequiredValue<StringRef>(TBDKey::Target, Obj, &Object::getString);
+ if (!TargetStr)
+ return make_error<JSONStubError>(getParseErrorMsg(TBDKey::Target));
+ auto TargetOrErr = Target::create(*TargetStr);
+ if (!TargetOrErr)
+ return make_error<JSONStubError>(getParseErrorMsg(TBDKey::Target));
+ IFTargets.push_back(*TargetOrErr);
+ // TODO: Implement Deployment Version.
+ }
+ return IFTargets;
+}
+
+Error collectSymbolsFromSegment(const Object *Segment, TargetsToSymbols &Result,
+ SymbolFlags SectionFlag) {
+ auto Err = collectFromArray(
+ TBDKey::Globals, Segment, [&Result, &SectionFlag](StringRef Name) {
+ JSONSymbol Sym = {SymbolKind::GlobalSymbol, Name.str(), SectionFlag};
+ Result.back().second.emplace_back(Sym);
+ });
+ if (Err)
+ return Err;
+
+ Err = collectFromArray(
+ TBDKey::ObjCClass, Segment, [&Result, &SectionFlag](StringRef Name) {
+ JSONSymbol Sym = {SymbolKind::ObjectiveCClass, Name.str(), SectionFlag};
+ Result.back().second.emplace_back(Sym);
+ });
+ if (Err)
+ return Err;
+
+ Err = collectFromArray(TBDKey::ObjCEHType, Segment,
+ [&Result, &SectionFlag](StringRef Name) {
+ JSONSymbol Sym = {SymbolKind::ObjectiveCClassEHType,
+ Name.str(), SectionFlag};
+ Result.back().second.emplace_back(Sym);
+ });
+ if (Err)
+ return Err;
+
+ Err = collectFromArray(
+ TBDKey::ObjCIvar, Segment, [&Result, &SectionFlag](StringRef Name) {
+ JSONSymbol Sym = {SymbolKind::ObjectiveCInstanceVariable, Name.str(),
+ SectionFlag};
+ Result.back().second.emplace_back(Sym);
+ });
+ if (Err)
+ return Err;
+
+ SymbolFlags WeakFlag = SectionFlag | (SectionFlag == SymbolFlags::Undefined
+ ? SymbolFlags::WeakReferenced
+ : SymbolFlags::WeakDefined);
+ Err = collectFromArray(TBDKey::Weak, Segment,
+ [&Result, WeakFlag](StringRef Name) {
+ JSONSymbol Sym = {
+ SymbolKind::GlobalSymbol,
+ Name.str(),
+ WeakFlag,
+ };
+ Result.back().second.emplace_back(Sym);
+ });
+ if (Err)
+ return Err;
+
+ Err = collectFromArray(
+ TBDKey::ThreadLocal, Segment, [&Result, SectionFlag](StringRef Name) {
+ JSONSymbol Sym = {SymbolKind::GlobalSymbol, Name.str(),
+ SymbolFlags::ThreadLocalValue | SectionFlag};
+ Result.back().second.emplace_back(Sym);
+ });
+ if (Err)
+ return Err;
+
+ return Error::success();
+}
+
+Expected<StringRef> getNameSection(const Object *File) {
+ const Array *Section = File->getArray(Keys[TBDKey::InstallName]);
+ if (!Section)
+ return make_error<JSONStubError>(getParseErrorMsg(TBDKey::InstallName));
+
+ assert(!Section->empty() && "unexpected missing install name");
+ // TODO: Just take first for now.
+ const auto *Obj = Section->front().getAsObject();
+ if (!Obj)
+ return make_error<JSONStubError>(getParseErrorMsg(TBDKey::InstallName));
+
+ return getRequiredValue<StringRef>(TBDKey::Name, Obj, &Object::getString);
+}
+
+Expected<TargetsToSymbols> getSymbolSection(const Object *File, TBDKey Key,
+ TargetList &Targets) {
+
+ const Array *Section = File->getArray(Keys[Key]);
+ if (!Section)
+ return TargetsToSymbols();
+
+ SymbolFlags SectionFlag;
+ switch (Key) {
+ case TBDKey::Reexports:
+ SectionFlag = SymbolFlags::Rexported;
+ break;
+ case TBDKey::Undefineds:
+ SectionFlag = SymbolFlags::Undefined;
+ break;
+ default:
+ SectionFlag = SymbolFlags::None;
+ break;
+ };
+
+ TargetsToSymbols Result;
+ TargetList MappedTargets;
+ for (auto Val : *Section) {
+ auto *Obj = Val.getAsObject();
+ if (!Obj)
+ continue;
+
+ auto TargetsOrErr = getTargets(Obj);
+ if (!TargetsOrErr) {
+ MappedTargets = Targets;
+ consumeError(TargetsOrErr.takeError());
+ } else {
+ MappedTargets = *TargetsOrErr;
+ }
+ Result.emplace_back(std::make_pair(Targets, std::vector<JSONSymbol>()));
+
+ auto *DataSection = Obj->getObject(Keys[TBDKey::Data]);
+ auto *TextSection = Obj->getObject(Keys[TBDKey::Text]);
+ // There should be at least one valid section.
+ if (!DataSection && !TextSection)
+ return make_error<JSONStubError>(getParseErrorMsg(Key));
+
+ if (DataSection) {
+ auto Err = collectSymbolsFromSegment(DataSection, Result, SectionFlag);
+ if (Err)
+ return Err;
+ }
+ if (TextSection) {
+ auto Err = collectSymbolsFromSegment(TextSection, Result, SectionFlag);
+ if (Err)
+ return Err;
+ }
+ }
+
+ return Result;
+}
+
+Expected<AttrToTargets> getLibSection(const Object *File, TBDKey Key,
+ TBDKey SubKey,
+ const TargetList &Targets) {
+ auto *Section = File->getArray(Keys[Key]);
+ if (!Section)
+ return AttrToTargets();
+
+ AttrToTargets Result;
+ TargetList MappedTargets;
+ for (auto Val : *Section) {
+ auto *Obj = Val.getAsObject();
+ if (!Obj)
+ continue;
+
+ auto TargetsOrErr = getTargets(Obj);
+ if (!TargetsOrErr) {
+ MappedTargets = Targets;
+ consumeError(TargetsOrErr.takeError());
+ } else {
+ MappedTargets = *TargetsOrErr;
+ }
+ auto Err =
+ collectFromArray(SubKey, Obj, [&Result, &MappedTargets](StringRef Key) {
+ Result[Key.str()] = MappedTargets;
+ });
+ if (Err)
+ return Err;
+ }
+
+ return Result;
+}
+
+Expected<AttrToTargets> getUmbrellaSection(const Object *File,
+ const TargetList &Targets) {
+ const auto *Umbrella = File->getArray(Keys[TBDKey::ParentUmbrella]);
+ if (!Umbrella)
+ return AttrToTargets();
+
+ AttrToTargets Result;
+ TargetList MappedTargets;
+ for (auto Val : *Umbrella) {
+ auto *Obj = Val.getAsObject();
+ if (!Obj)
+ return make_error<JSONStubError>(
+ getParseErrorMsg(TBDKey::ParentUmbrella));
+
+ // Get Targets section.
+ auto TargetsOrErr = getTargets(Obj);
+ if (!TargetsOrErr) {
+ MappedTargets = Targets;
+ consumeError(TargetsOrErr.takeError());
+ } else {
+ MappedTargets = *TargetsOrErr;
+ }
+
+ auto UmbrellaOrErr =
+ getRequiredValue<StringRef>(TBDKey::Umbrella, Obj, &Object::getString);
+ if (!UmbrellaOrErr)
+ return UmbrellaOrErr.takeError();
+ Result[UmbrellaOrErr->str()] = Targets;
+ }
+ return Result;
+}
+
+Expected<uint8_t> getSwiftVersion(const Object *File) {
+ const Array *Versions = File->getArray(Keys[TBDKey::SwiftABI]);
+ if (!Versions)
+ return 0;
+
+ for (const auto &Val : *Versions) {
+ const auto *Obj = Val.getAsObject();
+ if (!Obj)
+ return make_error<JSONStubError>(getParseErrorMsg(TBDKey::SwiftABI));
+
+ // TODO: Take first for now.
+ return getRequiredValue<int64_t, uint8_t>(TBDKey::ABI, Obj,
+ &Object::getInteger);
+ }
+
+ return 0;
+}
+
+Expected<PackedVersion> getPackedVersion(const Object *File, TBDKey Key) {
+ const Array *Versions = File->getArray(Keys[Key]);
+ if (!Versions)
+ return PackedVersion(1, 0, 0);
+
+ for (const auto &Val : *Versions) {
+ const auto *Obj = Val.getAsObject();
+ if (!Obj)
+ return make_error<JSONStubError>(getParseErrorMsg(Key));
+
+ auto ValidatePV = [](StringRef Version) -> std::optional<PackedVersion> {
+ PackedVersion PV;
+ auto [success, truncated] = PV.parse64(Version);
+ if (!success || truncated)
+ return std::nullopt;
+ return PV;
+ };
+ // TODO: Take first for now.
+ return getRequiredValue<StringRef, PackedVersion>(
+ TBDKey::Version, Obj, &Object::getString, PackedVersion(1, 0, 0),
+ ValidatePV);
+ }
+
+ return PackedVersion(1, 0, 0);
+}
+
+Expected<TBDFlags> getFlags(const Object *File) {
+ TBDFlags Flags = TBDFlags::None;
+ const Array *Section = File->getArray(Keys[TBDKey::Flags]);
+ if (!Section)
+ return Flags;
+
+ for (auto &Val : *Section) {
+ // TODO: Just take first for now.
+ const auto *Obj = Val.getAsObject();
+ if (!Obj)
+ return make_error<JSONStubError>(getParseErrorMsg(TBDKey::Flags));
+
+ auto FlagsOrErr =
+ collectFromArray(TBDKey::Attributes, Obj, [&Flags](StringRef Flag) {
+ TBDFlags TBDFlag =
+ StringSwitch<TBDFlags>(Flag)
+ .Case("flat_namespace", TBDFlags::FlatNamespace)
+ .Case("not_app_extension_safe",
+ TBDFlags::NotApplicationExtensionSafe)
+ .Default(TBDFlags::None);
+ Flags |= TBDFlag;
+ });
+
+ if (FlagsOrErr)
+ return FlagsOrErr;
+
+ return Flags;
+ }
+
+ return Flags;
+}
+
+using IFPtr = std::unique_ptr<InterfaceFile>;
+Expected<IFPtr> parseToInterfaceFile(const Object *File) {
+ auto TargetsOrErr = getTargetsSection(File);
+ if (!TargetsOrErr)
+ return TargetsOrErr.takeError();
+ TargetList Targets = *TargetsOrErr;
+
+ auto NameOrErr = getNameSection(File);
+ if (!NameOrErr)
+ return NameOrErr.takeError();
+ StringRef Name = *NameOrErr;
+
+ auto CurrVersionOrErr = getPackedVersion(File, TBDKey::CurrentVersion);
+ if (!CurrVersionOrErr)
+ return CurrVersionOrErr.takeError();
+ PackedVersion CurrVersion = *CurrVersionOrErr;
+
+ auto CompVersionOrErr = getPackedVersion(File, TBDKey::CompatibilityVersion);
+ if (!CompVersionOrErr)
+ return CompVersionOrErr.takeError();
+ PackedVersion CompVersion = *CompVersionOrErr;
+
+ auto SwiftABIOrErr = getSwiftVersion(File);
+ if (!SwiftABIOrErr)
+ return SwiftABIOrErr.takeError();
+ uint8_t SwiftABI = *SwiftABIOrErr;
+
+ auto FlagsOrErr = getFlags(File);
+ if (!FlagsOrErr)
+ return FlagsOrErr.takeError();
+ TBDFlags Flags = *FlagsOrErr;
+
+ auto UmbrellasOrErr = getUmbrellaSection(File, Targets);
+ if (!UmbrellasOrErr)
+ return UmbrellasOrErr.takeError();
+ AttrToTargets Umbrellas = *UmbrellasOrErr;
+
+ auto ClientsOrErr =
+ getLibSection(File, TBDKey::AllowableClients, TBDKey::Clients, Targets);
+ if (!ClientsOrErr)
+ return ClientsOrErr.takeError();
+ AttrToTargets Clients = *ClientsOrErr;
+
+ auto RLOrErr =
+ getLibSection(File, TBDKey::ReexportLibs, TBDKey::Names, Targets);
+ if (!RLOrErr)
+ return RLOrErr.takeError();
+ AttrToTargets ReexportLibs = std::move(*RLOrErr);
+
+ auto ExportsOrErr = getSymbolSection(File, TBDKey::Exports, Targets);
+ if (!ExportsOrErr)
+ return ExportsOrErr.takeError();
+ TargetsToSymbols Exports = std::move(*ExportsOrErr);
+
+ auto ReexportsOrErr = getSymbolSection(File, TBDKey::Reexports, Targets);
+ if (!ReexportsOrErr)
+ return ReexportsOrErr.takeError();
+ TargetsToSymbols Reexports = std::move(*ReexportsOrErr);
+
+ auto UndefinedsOrErr = getSymbolSection(File, TBDKey::Undefineds, Targets);
+ if (!UndefinedsOrErr)
+ return UndefinedsOrErr.takeError();
+ TargetsToSymbols Undefineds = std::move(*UndefinedsOrErr);
+
+ IFPtr F(new InterfaceFile);
+ F->setInstallName(Name);
+ F->setCurrentVersion(CurrVersion);
+ F->setCompatibilityVersion(CompVersion);
+ F->setSwiftABIVersion(SwiftABI);
+ F->setTwoLevelNamespace(!(Flags & TBDFlags::FlatNamespace));
+ F->setApplicationExtensionSafe(
+ !(Flags & TBDFlags::NotApplicationExtensionSafe));
+ for (auto &T : Targets)
+ F->addTarget(T);
+ for (auto &[Lib, Targets] : Clients)
+ for (auto Target : Targets)
+ F->addAllowableClient(Lib, Target);
+ for (auto &[Lib, Targets] : ReexportLibs)
+ for (auto Target : Targets)
+ F->addReexportedLibrary(Lib, Target);
+ for (auto &[Lib, Targets] : Umbrellas)
+ for (auto Target : Targets)
+ F->addParentUmbrella(Target, Lib);
+ for (auto &[Targets, Symbols] : Exports)
+ for (auto &Sym : Symbols)
+ F->addSymbol(Sym.Kind, Sym.Name, Targets, Sym.Flags);
+ for (auto &[Targets, Symbols] : Reexports)
+ for (auto &Sym : Symbols)
+ F->addSymbol(Sym.Kind, Sym.Name, Targets, Sym.Flags);
+ for (auto &[Targets, Symbols] : Undefineds)
+ for (auto &Sym : Symbols)
+ F->addSymbol(Sym.Kind, Sym.Name, Targets, Sym.Flags);
+
+ return F;
+}
+
+Expected<std::vector<IFPtr>> getInlinedLibs(const Object *File) {
+ std::vector<IFPtr> IFs;
+ const Array *Files = File->getArray(Keys[TBDKey::Documents]);
+ if (!Files)
+ return IFs;
+
+ for (auto Lib : *Files) {
+ auto IFOrErr = parseToInterfaceFile(Lib.getAsObject());
+ if (!IFOrErr)
+ return IFOrErr.takeError();
+ auto IF = std::move(*IFOrErr);
+ IFs.emplace_back(std::move(IF));
+ }
+ return IFs;
+}
+
+} // namespace StubParser
+
+Expected<std::unique_ptr<InterfaceFile>>
+MachO::getInterfaceFileFromJSON(StringRef JSON) {
+ auto ValOrErr = parse(JSON);
+ if (!ValOrErr)
+ return ValOrErr.takeError();
+
+ auto *Root = ValOrErr->getAsObject();
+ auto VersionOrErr = StubParser::getVersion(Root);
+ if (!VersionOrErr)
+ return VersionOrErr.takeError();
+ FileType Version = *VersionOrErr;
+
+ Object *MainLib = Root->getObject(Keys[TBDKey::MainLibrary]);
+ auto IFOrErr = StubParser::parseToInterfaceFile(MainLib);
+ if (!IFOrErr)
+ return IFOrErr.takeError();
+ (*IFOrErr)->setFileType(Version);
+ std::unique_ptr<InterfaceFile> IF(std::move(*IFOrErr));
+
+ auto IFsOrErr = StubParser::getInlinedLibs(Root);
+ if (!IFsOrErr)
+ return IFsOrErr.takeError();
+ for (auto &File : *IFsOrErr) {
+ File->setFileType(Version);
+ IF->addDocument(std::shared_ptr<InterfaceFile>(std::move(File)));
+ }
+ return std::move(IF);
+}
diff --git a/llvm/test/Object/Inputs/tapi-v4-watchos.tbd b/llvm/test/Object/Inputs/tapi-v4-watchos.tbd
index df0692273ed67..1a22de411079a 100644
--- a/llvm/test/Object/Inputs/tapi-v4-watchos.tbd
+++ b/llvm/test/Object/Inputs/tapi-v4-watchos.tbd
@@ -11,3 +11,4 @@ current-version: 1
exports:
- targets: [ armv7k-watchos-simulator, arm64_32-watchos-simulator ]
symbols: [ '_sym1' ]
+...
diff --git a/llvm/test/tools/llvm-tapi-
diff /Inputs/v4A.tbd b/llvm/test/tools/llvm-tapi-
diff /Inputs/v4A.tbd
index 7dba4d8430ca6..e4e6dcc180670 100644
--- a/llvm/test/tools/llvm-tapi-
diff /Inputs/v4A.tbd
+++ b/llvm/test/tools/llvm-tapi-
diff /Inputs/v4A.tbd
@@ -47,3 +47,4 @@ undefineds:
objc-ivars: []
weak-symbols: []
thread-local-symbols: []
+...
diff --git a/llvm/test/tools/llvm-tapi-
diff /Inputs/v4B.tbd b/llvm/test/tools/llvm-tapi-
diff /Inputs/v4B.tbd
index 6a831846951e7..9ad435c595e40 100644
--- a/llvm/test/tools/llvm-tapi-
diff /Inputs/v4B.tbd
+++ b/llvm/test/tools/llvm-tapi-
diff /Inputs/v4B.tbd
@@ -53,3 +53,4 @@ exports:
weak-symbols: [ _symC ]
- targets: [ x86_64-ios-simulator ]
symbols: [ _symB ]
+...
diff --git a/llvm/test/tools/llvm-tapi-
diff /Inputs/v4C.tbd b/llvm/test/tools/llvm-tapi-
diff /Inputs/v4C.tbd
index b2641e2d9e7a2..0ce4fb3d74ae0 100644
--- a/llvm/test/tools/llvm-tapi-
diff /Inputs/v4C.tbd
+++ b/llvm/test/tools/llvm-tapi-
diff /Inputs/v4C.tbd
@@ -47,3 +47,4 @@ undefineds:
objc-ivars: []
weak-symbols: []
thread-local-symbols: []
+...
diff --git a/llvm/test/tools/llvm-tapi-
diff /Inputs/v4D.tbd b/llvm/test/tools/llvm-tapi-
diff /Inputs/v4D.tbd
index 1338f62bddfc4..13b1416e1ce34 100644
--- a/llvm/test/tools/llvm-tapi-
diff /Inputs/v4D.tbd
+++ b/llvm/test/tools/llvm-tapi-
diff /Inputs/v4D.tbd
@@ -102,3 +102,4 @@ undefineds:
objc-ivars: []
weak-symbols: []
thread-local-symbols: []
+...
diff --git a/llvm/test/tools/llvm-tapi-
diff /Inputs/v4E.tbd b/llvm/test/tools/llvm-tapi-
diff /Inputs/v4E.tbd
index 1d34b2d44c260..b1b2d165ee123 100644
--- a/llvm/test/tools/llvm-tapi-
diff /Inputs/v4E.tbd
+++ b/llvm/test/tools/llvm-tapi-
diff /Inputs/v4E.tbd
@@ -53,3 +53,4 @@ exports:
weak-symbols: [ _symC ]
- targets: [ x86_64-ios-simulator ]
symbols: [ _symB ]
+...
diff --git a/llvm/unittests/TextAPI/CMakeLists.txt b/llvm/unittests/TextAPI/CMakeLists.txt
index d575d57610b96..2cb5be6feb1ec 100644
--- a/llvm/unittests/TextAPI/CMakeLists.txt
+++ b/llvm/unittests/TextAPI/CMakeLists.txt
@@ -7,6 +7,7 @@ add_llvm_unittest(TextAPITests
TextStubV2Tests.cpp
TextStubV3Tests.cpp
TextStubV4Tests.cpp
+ TextStubV5Tests.cpp
)
target_link_libraries(TextAPITests PRIVATE LLVMTestingSupport)
diff --git a/llvm/unittests/TextAPI/TextStubHelpers.h b/llvm/unittests/TextAPI/TextStubHelpers.h
index 67bfa1aba2b70..c59252c7ed176 100644
--- a/llvm/unittests/TextAPI/TextStubHelpers.h
+++ b/llvm/unittests/TextAPI/TextStubHelpers.h
@@ -16,10 +16,11 @@
namespace llvm {
struct ExportedSymbol {
- llvm::MachO::SymbolKind Kind;
- std::string Name;
- bool WeakDefined;
- bool ThreadLocalValue;
+ MachO::SymbolKind Kind = MachO::SymbolKind::GlobalSymbol;
+ std::string Name = {};
+ bool Weak = false;
+ bool ThreadLocalValue = false;
+ MachO::TargetList Targets = {};
};
using ExportedSymbolSeq = std::vector<ExportedSymbol>;
@@ -32,8 +33,8 @@ inline bool operator<(const ExportedSymbol &LHS, const ExportedSymbol &RHS) {
}
inline bool operator==(const ExportedSymbol &LHS, const ExportedSymbol &RHS) {
- return std::tie(LHS.Kind, LHS.Name, LHS.WeakDefined, LHS.ThreadLocalValue) ==
- std::tie(RHS.Kind, RHS.Name, RHS.WeakDefined, RHS.ThreadLocalValue);
+ return std::tie(LHS.Kind, LHS.Name, LHS.Weak, LHS.ThreadLocalValue) ==
+ std::tie(RHS.Kind, RHS.Name, RHS.Weak, RHS.ThreadLocalValue);
}
inline std::string stripWhitespace(std::string S) {
diff --git a/llvm/unittests/TextAPI/TextStubV4Tests.cpp b/llvm/unittests/TextAPI/TextStubV4Tests.cpp
index 570e853f35067..ac519500e54ca 100644
--- a/llvm/unittests/TextAPI/TextStubV4Tests.cpp
+++ b/llvm/unittests/TextAPI/TextStubV4Tests.cpp
@@ -903,7 +903,8 @@ TEST(TBDv4, MalformedFile2) {
"tbd-version: 4\n"
"targets: [ x86_64-macos ]\n"
"install-name: Test.dylib\n"
- "foobar: \"unsupported key\"\n";
+ "foobar: \"unsupported key\"\n"
+ "...\n";
Expected<TBDFile> Result =
TextAPIReader::get(MemoryBufferRef(TBDv4MalformedFile2, "Test.tbd"));
diff --git a/llvm/unittests/TextAPI/TextStubV5Tests.cpp b/llvm/unittests/TextAPI/TextStubV5Tests.cpp
new file mode 100644
index 0000000000000..26f477a8b6686
--- /dev/null
+++ b/llvm/unittests/TextAPI/TextStubV5Tests.cpp
@@ -0,0 +1,488 @@
+//===-- TextStubV5Tests.cpp - TBD V5 File Test ----------------------------===//
+//
+// 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
+//
+//===-----------------------------------------------------------------------===/
+
+#include "TextStubHelpers.h"
+#include "llvm/TextAPI/InterfaceFile.h"
+#include "llvm/TextAPI/TextAPIReader.h"
+#include "llvm/TextAPI/TextAPIWriter.h"
+#include "gtest/gtest.h"
+#include <string>
+#include <vector>
+
+using namespace llvm;
+using namespace llvm::MachO;
+
+namespace TBDv5 {
+
+TEST(TBDv5, ReadFile) {
+ 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": [
+ "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());
+ EXPECT_EQ(FileType::TBD_V5, File->getFileType());
+ EXPECT_EQ(std::string("/S/L/F/Foo.framework/Foo"), File->getInstallName());
+
+ TargetList AllTargets = {
+ Target(AK_x86_64, PLATFORM_MACOS),
+ Target(AK_arm64, PLATFORM_MACOS),
+ Target(AK_arm64, PLATFORM_MACCATALYST),
+ };
+ EXPECT_EQ(mapToPlatformSet(AllTargets), File->getPlatforms());
+ EXPECT_EQ(mapToArchitectureSet(AllTargets), File->getArchitectures());
+
+ EXPECT_EQ(PackedVersion(1, 2, 0), File->getCurrentVersion());
+ EXPECT_EQ(PackedVersion(1, 1, 0), File->getCompatibilityVersion());
+ EXPECT_TRUE(File->isApplicationExtensionSafe());
+ EXPECT_FALSE(File->isTwoLevelNamespace());
+ EXPECT_EQ(0U, File->documents().size());
+
+ InterfaceFileRef ClientA("ClientA", AllTargets);
+ InterfaceFileRef ClientB("ClientB", AllTargets);
+ EXPECT_EQ(2U, File->allowableClients().size());
+ EXPECT_EQ(ClientA, File->allowableClients().at(0));
+ EXPECT_EQ(ClientB, File->allowableClients().at(1));
+
+ InterfaceFileRef ReexportA("/u/l/l/libbar.dylib", AllTargets);
+ InterfaceFileRef ReexportB("/u/l/l/libfoo.dylib", AllTargets);
+ EXPECT_EQ(2U, File->reexportedLibraries().size());
+ EXPECT_EQ(ReexportA, File->reexportedLibraries().at(0));
+ EXPECT_EQ(ReexportB, File->reexportedLibraries().at(1));
+
+ std::vector<std::pair<Target, std::string>> Umbrellas = {
+ {Target(AK_x86_64, PLATFORM_MACOS), "System"},
+ {Target(AK_arm64, PLATFORM_MACOS), "System"},
+ {Target(AK_arm64, PLATFORM_MACCATALYST), "System"}};
+ EXPECT_EQ(Umbrellas, File->umbrellas());
+
+ ExportedSymbolSeq Exports, Reexports, Undefineds;
+ for (const auto *Sym : File->symbols()) {
+ TargetList SymTargets{Sym->targets().begin(), Sym->targets().end()};
+ ExportedSymbol Temp =
+ ExportedSymbol{Sym->getKind(), std::string(Sym->getName()),
+ Sym->isWeakDefined() || Sym->isWeakReferenced(),
+ Sym->isThreadLocalValue(), 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, MacOSTargets},
+ {SymbolKind::GlobalSymbol,
+ "_funcFoo",
+ false,
+ false,
+ {Target(AK_x86_64, PLATFORM_MACOS)}},
+ {SymbolKind::GlobalSymbol, "_global", false, false, MacOSTargets},
+ {SymbolKind::GlobalSymbol,
+ "_globalVar",
+ false,
+ false,
+ {Target(AK_x86_64, PLATFORM_MACOS)}},
+ {SymbolKind::ObjectiveCClass, "ClassA", false, false, MacOSTargets},
+ {SymbolKind::ObjectiveCClass,
+ "ClassData",
+ false,
+ false,
+ {Target(AK_x86_64, PLATFORM_MACOS)}},
+ {SymbolKind::ObjectiveCClassEHType,
+ "ClassA",
+ false,
+ false,
+ {Target(AK_x86_64, PLATFORM_MACOS)}},
+ {SymbolKind::ObjectiveCClassEHType,
+ "ClassB",
+ false,
+ false,
+ {Target(AK_x86_64, PLATFORM_MACOS)}},
+ {SymbolKind::ObjectiveCInstanceVariable,
+ "ClassA.ivar1",
+ false,
+ false,
+ {Target(AK_x86_64, PLATFORM_MACOS)}},
+ {SymbolKind::ObjectiveCInstanceVariable,
+ "ClassA.ivar2",
+ false,
+ false,
+ {Target(AK_x86_64, PLATFORM_MACOS)}},
+ {SymbolKind::ObjectiveCInstanceVariable,
+ "ClassC.ivar1",
+ false,
+ false,
+ {Target(AK_x86_64, PLATFORM_MACOS)}},
+ };
+ std::vector<ExportedSymbol> ExpectedReexportedSymbols = {
+ {SymbolKind::GlobalSymbol, "_funcA", false, false, MacOSTargets},
+ {SymbolKind::GlobalSymbol, "_globalRe", false, false, MacOSTargets},
+ {SymbolKind::ObjectiveCClass, "ClassRexport", false, false, MacOSTargets},
+ };
+
+ std::vector<ExportedSymbol> ExpectedUndefinedSymbols = {
+ {SymbolKind::GlobalSymbol,
+ "_globalBind",
+ false,
+ false,
+ {Target(AK_x86_64, PLATFORM_MACOS)}},
+ {SymbolKind::GlobalSymbol,
+ "referenced_sym",
+ true,
+ false,
+ {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, ReadMultipleTargets) {
+ static const char TBDv5File[] = R"({
+"tapi_tbd_version": 5,
+"main_library": {
+ "target_info": [
+ {
+ "target": "x86_64-macos",
+ "min_deployment": "10.14"
+ },
+ {
+ "target": "arm64-macos",
+ "min_deployment": "10.14"
+ },
+ {
+ "target": "arm64-maccatalyst",
+ "min_deployment": "12.1"
+ }
+ ],
+ "install_names":[
+ { "name":"/usr/lib/libFoo.dylib" }
+ ],
+ "swift_abi":[ { "abi":8 } ],
+ "reexported_libraries": [
+ {
+ "targets": [ "x86_64-maccatalyst" ],
+ "names": [
+ "/u/l/l/libfoo.dylib",
+ "/u/l/l/libbar.dylib"
+ ]
+ },
+ {
+ "targets": [ "arm64-maccatalyst" ],
+ "names": [ "/u/l/l/libArmOnly.dylib" ]
+ }
+ ]
+}
+})";
+
+ Expected<TBDFile> Result =
+ TextAPIReader::get(MemoryBufferRef(TBDv5File, "Test.tbd"));
+ EXPECT_TRUE(!!Result);
+ TBDFile File = std::move(Result.get());
+ EXPECT_EQ(FileType::TBD_V5, File->getFileType());
+ EXPECT_EQ(std::string("/usr/lib/libFoo.dylib"), File->getInstallName());
+ EXPECT_TRUE(File->isApplicationExtensionSafe());
+ EXPECT_TRUE(File->isTwoLevelNamespace());
+ EXPECT_EQ(PackedVersion(1, 0, 0), File->getCurrentVersion());
+ EXPECT_EQ(PackedVersion(1, 0, 0), File->getCompatibilityVersion());
+ EXPECT_EQ(8U, File->getSwiftABIVersion());
+
+ TargetList AllTargets = {
+ Target(AK_x86_64, PLATFORM_MACOS),
+ Target(AK_arm64, PLATFORM_MACOS),
+ Target(AK_arm64, PLATFORM_MACCATALYST),
+ };
+ EXPECT_EQ(mapToPlatformSet(AllTargets), File->getPlatforms());
+ EXPECT_EQ(mapToArchitectureSet(AllTargets), File->getArchitectures());
+
+ InterfaceFileRef ReexportA("/u/l/l/libArmOnly.dylib",
+ {Target(AK_arm64, PLATFORM_MACCATALYST)});
+ InterfaceFileRef ReexportB("/u/l/l/libbar.dylib",
+ {Target(AK_x86_64, PLATFORM_MACCATALYST)});
+ InterfaceFileRef ReexportC("/u/l/l/libfoo.dylib",
+ {Target(AK_x86_64, PLATFORM_MACCATALYST)});
+ EXPECT_EQ(3U, File->reexportedLibraries().size());
+ EXPECT_EQ(ReexportA, File->reexportedLibraries().at(0));
+ EXPECT_EQ(ReexportB, File->reexportedLibraries().at(1));
+ EXPECT_EQ(ReexportC, File->reexportedLibraries().at(2));
+}
+
+TEST(TBDv5, ReadMultipleDocuments) {
+ static const char TBDv5File[] = R"({
+"tapi_tbd_version": 5,
+"main_library": {
+ "target_info": [
+ {
+ "target": "armv7-ios",
+ "min_deployment": "11.0"
+ }
+ ],
+ "install_names":[
+ { "name":"/S/L/F/Foo.framework/Foo" }
+ ],
+ "reexported_libraries": [
+ { "names": ["/u/l/l/libfoo.dylib"] }
+ ]
+},
+"libraries": [
+ {
+ "target_info": [
+ {
+ "target": "armv7-ios",
+ "min_deployment": "11.0"
+ }
+ ],
+ "install_names":[
+ { "name":"/u/l/l/libfoo.dylib" }
+ ],
+ "flags":[
+ { "attributes": ["not_app_extension_safe"] }
+ ],
+ "exported_symbols": [
+ {
+ "data": {
+ "thread_local": [ "_globalVar" ],
+ "objc_class": [ "ClassData" ],
+ "objc_eh_type": [ "ClassA", "ClassB" ]
+ },
+ "text": {
+ "global": [ "_funcFoo" ]
+ }
+ }
+ ]
+ }
+]})";
+
+ Expected<TBDFile> Result =
+ TextAPIReader::get(MemoryBufferRef(TBDv5File, "Test.tbd"));
+ EXPECT_TRUE(!!Result);
+ TBDFile File = std::move(Result.get());
+ EXPECT_EQ(FileType::TBD_V5, File->getFileType());
+ EXPECT_EQ(std::string("/S/L/F/Foo.framework/Foo"), File->getInstallName());
+ EXPECT_TRUE(File->isTwoLevelNamespace());
+ EXPECT_TRUE(File->isApplicationExtensionSafe());
+
+ TargetList Targets(File->targets().begin(), File->targets().end());
+ Target iOSTarget(AK_armv7, PLATFORM_IOS);
+ EXPECT_EQ(TargetList{iOSTarget}, Targets);
+ std::vector<const Symbol *> Symbols(File->symbols().begin(),
+ File->symbols().end());
+ EXPECT_EQ(0U, Symbols.size());
+
+ InterfaceFileRef Reexport("/u/l/l/libfoo.dylib", {iOSTarget});
+ EXPECT_EQ(1U, File->reexportedLibraries().size());
+ EXPECT_EQ(Reexport, File->reexportedLibraries().at(0));
+
+ // Check inlined library.
+ EXPECT_EQ(1U, File->documents().size());
+ TBDReexportFile Document = File->documents().front();
+ Targets = {Document->targets().begin(), Document->targets().end()};
+ EXPECT_EQ(TargetList{iOSTarget}, Targets);
+ EXPECT_EQ(std::string("/u/l/l/libfoo.dylib"), Document->getInstallName());
+ EXPECT_EQ(0U, Document->getSwiftABIVersion());
+ EXPECT_TRUE(Document->isTwoLevelNamespace());
+ EXPECT_FALSE(Document->isApplicationExtensionSafe());
+
+ ExportedSymbolSeq Exports;
+ for (const auto *Sym : Document->symbols())
+ Exports.emplace_back(
+ ExportedSymbol{Sym->getKind(),
+ std::string(Sym->getName()),
+ Sym->isWeakDefined() || Sym->isWeakReferenced(),
+ Sym->isThreadLocalValue(),
+ {iOSTarget}});
+
+ ExportedSymbolSeq ExpectedExports = {
+ {SymbolKind::GlobalSymbol, "_globalVar", false, true, {iOSTarget}},
+ {SymbolKind::ObjectiveCClass, "ClassData", false, false, {iOSTarget}},
+ {SymbolKind::ObjectiveCClassEHType, "ClassB", false, false, {iOSTarget}},
+ {SymbolKind::ObjectiveCClassEHType, "ClassA", false, false, {iOSTarget}},
+ {SymbolKind::GlobalSymbol, "_funcFoo", false, false, {iOSTarget}},
+ };
+
+ 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