Reland "[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 will be followed up in future PRs. Reviewed By: pete Differential Revision: https://reviews.llvm.org/D144156
This commit is contained in:
@@ -22,3 +22,4 @@ current-version: 1.0.0
|
||||
exports:
|
||||
- targets: [ arm64-ios-simulator ]
|
||||
symbols: [ _arm64_sim_only ]
|
||||
...
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ]
|
||||
---
|
||||
...
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -2,6 +2,7 @@ add_llvm_component_library(LLVMTextAPI
|
||||
Architecture.cpp
|
||||
ArchitectureSet.cpp
|
||||
InterfaceFile.cpp
|
||||
TextStubV5.cpp
|
||||
PackedVersion.cpp
|
||||
Platform.cpp
|
||||
Symbol.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.
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Implememts common Text Stub YAML mappings.
|
||||
// Implements common Text Stub YAML mappings.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
|
||||
@@ -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> {
|
||||
|
||||
700
llvm/lib/TextAPI/TextStubV5.cpp
Normal file
700
llvm/lib/TextAPI/TextStubV5.cpp
Normal file
@@ -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 std::move(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 std::move(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 std::move(Err);
|
||||
}
|
||||
if (TextSection) {
|
||||
auto Err = collectSymbolsFromSegment(TextSection, Result, SectionFlag);
|
||||
if (Err)
|
||||
return std::move(Err);
|
||||
}
|
||||
}
|
||||
|
||||
return std::move(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 std::move(Err);
|
||||
}
|
||||
|
||||
return std::move(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 std::move(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 std::move(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 std::move(F);
|
||||
}
|
||||
|
||||
Expected<std::vector<IFPtr>> getInlinedLibs(const Object *File) {
|
||||
std::vector<IFPtr> IFs;
|
||||
const Array *Files = File->getArray(Keys[TBDKey::Documents]);
|
||||
if (!Files)
|
||||
return std::move(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 std::move(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);
|
||||
}
|
||||
@@ -11,3 +11,4 @@ current-version: 1
|
||||
exports:
|
||||
- targets: [ armv7k-watchos-simulator, arm64_32-watchos-simulator ]
|
||||
symbols: [ '_sym1' ]
|
||||
...
|
||||
|
||||
@@ -47,3 +47,4 @@ undefineds:
|
||||
objc-ivars: []
|
||||
weak-symbols: []
|
||||
thread-local-symbols: []
|
||||
...
|
||||
|
||||
@@ -53,3 +53,4 @@ exports:
|
||||
weak-symbols: [ _symC ]
|
||||
- targets: [ x86_64-ios-simulator ]
|
||||
symbols: [ _symB ]
|
||||
...
|
||||
|
||||
@@ -47,3 +47,4 @@ undefineds:
|
||||
objc-ivars: []
|
||||
weak-symbols: []
|
||||
thread-local-symbols: []
|
||||
...
|
||||
|
||||
@@ -102,3 +102,4 @@ undefineds:
|
||||
objc-ivars: []
|
||||
weak-symbols: []
|
||||
thread-local-symbols: []
|
||||
...
|
||||
|
||||
@@ -53,3 +53,4 @@ exports:
|
||||
weak-symbols: [ _symC ]
|
||||
- targets: [ x86_64-ios-simulator ]
|
||||
symbols: [ _symB ]
|
||||
...
|
||||
|
||||
@@ -7,6 +7,7 @@ add_llvm_unittest(TextAPITests
|
||||
TextStubV2Tests.cpp
|
||||
TextStubV3Tests.cpp
|
||||
TextStubV4Tests.cpp
|
||||
TextStubV5Tests.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(TextAPITests PRIVATE LLVMTestingSupport)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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"));
|
||||
|
||||
489
llvm/unittests/TextAPI/TextStubV5Tests.cpp
Normal file
489
llvm/unittests/TextAPI/TextStubV5Tests.cpp
Normal file
@@ -0,0 +1,489 @@
|
||||
//===-- 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}});
|
||||
|
||||
llvm::sort(Exports);
|
||||
ExportedSymbolSeq ExpectedExports = {
|
||||
{SymbolKind::GlobalSymbol, "_funcFoo", false, false, {iOSTarget}},
|
||||
{SymbolKind::GlobalSymbol, "_globalVar", false, true, {iOSTarget}},
|
||||
{SymbolKind::ObjectiveCClass, "ClassData", false, false, {iOSTarget}},
|
||||
{SymbolKind::ObjectiveCClassEHType, "ClassA", false, false, {iOSTarget}},
|
||||
{SymbolKind::ObjectiveCClassEHType, "ClassB", false, false, {iOSTarget}},
|
||||
};
|
||||
|
||||
EXPECT_EQ(ExpectedExports.size(), Exports.size());
|
||||
EXPECT_TRUE(
|
||||
std::equal(Exports.begin(), Exports.end(), std::begin(ExpectedExports)));
|
||||
}
|
||||
|
||||
} // end namespace TBDv5
|
||||
Reference in New Issue
Block a user