| //===- TextStub.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 the text stub file reader/writer. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "TextAPIContext.h" | |
| #include "TextStubCommon.h" | |
| #include "llvm/ADT/BitmaskEnum.h" | |
| #include "llvm/ADT/SmallString.h" | |
| #include "llvm/ADT/StringRef.h" | |
| #include "llvm/Support/Allocator.h" | |
| #include "llvm/Support/SourceMgr.h" | |
| #include "llvm/Support/YAMLTraits.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| #include "llvm/TextAPI/Architecture.h" | |
| #include "llvm/TextAPI/ArchitectureSet.h" | |
| #include "llvm/TextAPI/InterfaceFile.h" | |
| #include "llvm/TextAPI/PackedVersion.h" | |
| #include "llvm/TextAPI/TextAPIReader.h" | |
| #include "llvm/TextAPI/TextAPIWriter.h" | |
| #include <algorithm> | |
| #include <set> | |
| // clang-format off | |
| /* | |
| YAML Format specification. | |
| The TBD v1 format only support two level address libraries and is per | |
| definition application extension safe. | |
| --- # the tag !tapi-tbd-v1 is optional and | |
| # shouldn't be emitted to support older linker. | |
| archs: [ armv7, armv7s, arm64 ] # the list of architecture slices that are | |
| # supported by this file. | |
| platform: ios # Specifies the platform (macosx, ios, etc) | |
| install-name: /u/l/libfoo.dylib # | |
| current-version: 1.2.3 # Optional: defaults to 1.0 | |
| compatibility-version: 1.0 # Optional: defaults to 1.0 | |
| swift-version: 0 # Optional: defaults to 0 | |
| objc-constraint: none # Optional: defaults to none | |
| exports: # List of export sections | |
| ... | |
| Each export section is defined as following: | |
| - archs: [ arm64 ] # the list of architecture slices | |
| allowed-clients: [ client ] # Optional: List of clients | |
| re-exports: [ ] # Optional: List of re-exports | |
| symbols: [ _sym ] # Optional: List of symbols | |
| objc-classes: [] # Optional: List of Objective-C classes | |
| objc-ivars: [] # Optional: List of Objective C Instance | |
| # Variables | |
| weak-def-symbols: [] # Optional: List of weak defined symbols | |
| thread-local-symbols: [] # Optional: List of thread local symbols | |
| */ | |
| /* | |
| YAML Format specification. | |
| --- !tapi-tbd-v2 | |
| archs: [ armv7, armv7s, arm64 ] # the list of architecture slices that are | |
| # supported by this file. | |
| uuids: [ armv7:... ] # Optional: List of architecture and UUID pairs. | |
| platform: ios # Specifies the platform (macosx, ios, etc) | |
| flags: [] # Optional: | |
| install-name: /u/l/libfoo.dylib # | |
| current-version: 1.2.3 # Optional: defaults to 1.0 | |
| compatibility-version: 1.0 # Optional: defaults to 1.0 | |
| swift-version: 0 # Optional: defaults to 0 | |
| objc-constraint: retain_release # Optional: defaults to retain_release | |
| parent-umbrella: # Optional: | |
| exports: # List of export sections | |
| ... | |
| undefineds: # List of undefineds sections | |
| ... | |
| Each export section is defined as following: | |
| - archs: [ arm64 ] # the list of architecture slices | |
| allowed-clients: [ client ] # Optional: List of clients | |
| re-exports: [ ] # Optional: List of re-exports | |
| symbols: [ _sym ] # Optional: List of symbols | |
| objc-classes: [] # Optional: List of Objective-C classes | |
| objc-ivars: [] # Optional: List of Objective C Instance | |
| # Variables | |
| weak-def-symbols: [] # Optional: List of weak defined symbols | |
| thread-local-symbols: [] # Optional: List of thread local symbols | |
| Each undefineds section is defined as following: | |
| - archs: [ arm64 ] # the list of architecture slices | |
| symbols: [ _sym ] # Optional: List of symbols | |
| objc-classes: [] # Optional: List of Objective-C classes | |
| objc-ivars: [] # Optional: List of Objective C Instance Variables | |
| weak-ref-symbols: [] # Optional: List of weak defined symbols | |
| */ | |
| /* | |
| YAML Format specification. | |
| --- !tapi-tbd-v3 | |
| archs: [ armv7, armv7s, arm64 ] # the list of architecture slices that are | |
| # supported by this file. | |
| uuids: [ armv7:... ] # Optional: List of architecture and UUID pairs. | |
| platform: ios # Specifies the platform (macosx, ios, etc) | |
| flags: [] # Optional: | |
| install-name: /u/l/libfoo.dylib # | |
| current-version: 1.2.3 # Optional: defaults to 1.0 | |
| compatibility-version: 1.0 # Optional: defaults to 1.0 | |
| swift-abi-version: 0 # Optional: defaults to 0 | |
| objc-constraint: retain_release # Optional: defaults to retain_release | |
| parent-umbrella: # Optional: | |
| exports: # List of export sections | |
| ... | |
| undefineds: # List of undefineds sections | |
| ... | |
| Each export section is defined as following: | |
| - archs: [ arm64 ] # the list of architecture slices | |
| allowed-clients: [ client ] # Optional: List of clients | |
| re-exports: [ ] # Optional: List of re-exports | |
| symbols: [ _sym ] # Optional: List of symbols | |
| objc-classes: [] # Optional: List of Objective-C classes | |
| objc-eh-types: [] # Optional: List of Objective-C classes | |
| # with EH | |
| objc-ivars: [] # Optional: List of Objective C Instance | |
| # Variables | |
| weak-def-symbols: [] # Optional: List of weak defined symbols | |
| thread-local-symbols: [] # Optional: List of thread local symbols | |
| Each undefineds section is defined as following: | |
| - archs: [ arm64 ] # the list of architecture slices | |
| symbols: [ _sym ] # Optional: List of symbols | |
| objc-classes: [] # Optional: List of Objective-C classes | |
| objc-eh-types: [] # Optional: List of Objective-C classes | |
| # with EH | |
| objc-ivars: [] # Optional: List of Objective C Instance Variables | |
| weak-ref-symbols: [] # Optional: List of weak defined symbols | |
| */ | |
| /* | |
| YAML Format specification. | |
| --- !tapi-tbd | |
| tbd-version: 4 # The tbd version for format | |
| targets: [ armv7-ios, x86_64-maccatalyst ] # The list of applicable tapi supported target triples | |
| uuids: # Optional: List of target and UUID pairs. | |
| - target: armv7-ios | |
| value: ... | |
| - target: x86_64-maccatalyst | |
| value: ... | |
| flags: [] # Optional: | |
| install-name: /u/l/libfoo.dylib # | |
| current-version: 1.2.3 # Optional: defaults to 1.0 | |
| compatibility-version: 1.0 # Optional: defaults to 1.0 | |
| swift-abi-version: 0 # Optional: defaults to 0 | |
| parent-umbrella: # Optional: | |
| allowable-clients: | |
| - targets: [ armv7-ios ] # Optional: | |
| clients: [ clientA ] | |
| exports: # List of export sections | |
| ... | |
| re-exports: # List of reexport sections | |
| ... | |
| undefineds: # List of undefineds sections | |
| ... | |
| Each export and reexport section is defined as following: | |
| - targets: [ arm64-macos ] # The list of target triples associated with symbols | |
| symbols: [ _symA ] # Optional: List of symbols | |
| objc-classes: [] # Optional: List of Objective-C classes | |
| objc-eh-types: [] # Optional: List of Objective-C classes | |
| # with EH | |
| objc-ivars: [] # Optional: List of Objective C Instance | |
| # Variables | |
| weak-symbols: [] # Optional: List of weak defined symbols | |
| thread-local-symbols: [] # Optional: List of thread local symbols | |
| - targets: [ arm64-macos, x86_64-maccatalyst ] # Optional: Targets for applicable additional symbols | |
| symbols: [ _symB ] # Optional: List of symbols | |
| Each undefineds section is defined as following: | |
| - targets: [ arm64-macos ] # The list of target triples associated with symbols | |
| symbols: [ _symC ] # Optional: List of symbols | |
| objc-classes: [] # Optional: List of Objective-C classes | |
| objc-eh-types: [] # Optional: List of Objective-C classes | |
| # with EH | |
| objc-ivars: [] # Optional: List of Objective C Instance Variables | |
| weak-symbols: [] # Optional: List of weak defined symbols | |
| */ | |
| // clang-format on | |
| using namespace llvm; | |
| using namespace llvm::yaml; | |
| using namespace llvm::MachO; | |
| namespace { | |
| struct ExportSection { | |
| std::vector<Architecture> Architectures; | |
| std::vector<FlowStringRef> AllowableClients; | |
| std::vector<FlowStringRef> ReexportedLibraries; | |
| std::vector<FlowStringRef> Symbols; | |
| std::vector<FlowStringRef> Classes; | |
| std::vector<FlowStringRef> ClassEHs; | |
| std::vector<FlowStringRef> IVars; | |
| std::vector<FlowStringRef> WeakDefSymbols; | |
| std::vector<FlowStringRef> TLVSymbols; | |
| }; | |
| struct UndefinedSection { | |
| std::vector<Architecture> Architectures; | |
| std::vector<FlowStringRef> Symbols; | |
| std::vector<FlowStringRef> Classes; | |
| std::vector<FlowStringRef> ClassEHs; | |
| std::vector<FlowStringRef> IVars; | |
| std::vector<FlowStringRef> WeakRefSymbols; | |
| }; | |
| // Sections for direct target mapping in TBDv4 | |
| struct SymbolSection { | |
| TargetList Targets; | |
| std::vector<FlowStringRef> Symbols; | |
| std::vector<FlowStringRef> Classes; | |
| std::vector<FlowStringRef> ClassEHs; | |
| std::vector<FlowStringRef> Ivars; | |
| std::vector<FlowStringRef> WeakSymbols; | |
| std::vector<FlowStringRef> TlvSymbols; | |
| }; | |
| struct MetadataSection { | |
| enum Option { Clients, Libraries }; | |
| std::vector<Target> Targets; | |
| std::vector<FlowStringRef> Values; | |
| }; | |
| struct UmbrellaSection { | |
| std::vector<Target> Targets; | |
| std::string Umbrella; | |
| }; | |
| // UUID's for TBDv4 are mapped to target not arch | |
| struct UUIDv4 { | |
| Target TargetID; | |
| std::string Value; | |
| UUIDv4() = default; | |
| 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) | |
| LLVM_YAML_IS_SEQUENCE_VECTOR(ExportSection) | |
| LLVM_YAML_IS_SEQUENCE_VECTOR(UndefinedSection) | |
| // Specific to TBDv4 | |
| LLVM_YAML_IS_SEQUENCE_VECTOR(SymbolSection) | |
| LLVM_YAML_IS_SEQUENCE_VECTOR(MetadataSection) | |
| LLVM_YAML_IS_SEQUENCE_VECTOR(UmbrellaSection) | |
| LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(Target) | |
| LLVM_YAML_IS_SEQUENCE_VECTOR(UUIDv4) | |
| namespace llvm { | |
| namespace yaml { | |
| template <> struct MappingTraits<ExportSection> { | |
| static void mapping(IO &IO, ExportSection &Section) { | |
| const auto *Ctx = reinterpret_cast<TextAPIContext *>(IO.getContext()); | |
| assert((!Ctx || (Ctx && Ctx->FileKind != FileType::Invalid)) && | |
| "File type is not set in YAML context"); | |
| IO.mapRequired("archs", Section.Architectures); | |
| if (Ctx->FileKind == FileType::TBD_V1) | |
| IO.mapOptional("allowed-clients", Section.AllowableClients); | |
| else | |
| IO.mapOptional("allowable-clients", Section.AllowableClients); | |
| IO.mapOptional("re-exports", Section.ReexportedLibraries); | |
| IO.mapOptional("symbols", Section.Symbols); | |
| IO.mapOptional("objc-classes", Section.Classes); | |
| if (Ctx->FileKind == FileType::TBD_V3) | |
| IO.mapOptional("objc-eh-types", Section.ClassEHs); | |
| IO.mapOptional("objc-ivars", Section.IVars); | |
| IO.mapOptional("weak-def-symbols", Section.WeakDefSymbols); | |
| IO.mapOptional("thread-local-symbols", Section.TLVSymbols); | |
| } | |
| }; | |
| template <> struct MappingTraits<UndefinedSection> { | |
| static void mapping(IO &IO, UndefinedSection &Section) { | |
| const auto *Ctx = reinterpret_cast<TextAPIContext *>(IO.getContext()); | |
| assert((!Ctx || (Ctx && Ctx->FileKind != FileType::Invalid)) && | |
| "File type is not set in YAML context"); | |
| IO.mapRequired("archs", Section.Architectures); | |
| IO.mapOptional("symbols", Section.Symbols); | |
| IO.mapOptional("objc-classes", Section.Classes); | |
| if (Ctx->FileKind == FileType::TBD_V3) | |
| IO.mapOptional("objc-eh-types", Section.ClassEHs); | |
| IO.mapOptional("objc-ivars", Section.IVars); | |
| IO.mapOptional("weak-ref-symbols", Section.WeakRefSymbols); | |
| } | |
| }; | |
| template <> struct MappingTraits<SymbolSection> { | |
| static void mapping(IO &IO, SymbolSection &Section) { | |
| IO.mapRequired("targets", Section.Targets); | |
| IO.mapOptional("symbols", Section.Symbols); | |
| IO.mapOptional("objc-classes", Section.Classes); | |
| IO.mapOptional("objc-eh-types", Section.ClassEHs); | |
| IO.mapOptional("objc-ivars", Section.Ivars); | |
| IO.mapOptional("weak-symbols", Section.WeakSymbols); | |
| IO.mapOptional("thread-local-symbols", Section.TlvSymbols); | |
| } | |
| }; | |
| template <> struct MappingTraits<UmbrellaSection> { | |
| static void mapping(IO &IO, UmbrellaSection &Section) { | |
| IO.mapRequired("targets", Section.Targets); | |
| IO.mapRequired("umbrella", Section.Umbrella); | |
| } | |
| }; | |
| template <> struct MappingTraits<UUIDv4> { | |
| static void mapping(IO &IO, UUIDv4 &UUID) { | |
| IO.mapRequired("target", UUID.TargetID); | |
| IO.mapRequired("value", UUID.Value); | |
| } | |
| }; | |
| template <> | |
| struct MappingContextTraits<MetadataSection, MetadataSection::Option> { | |
| static void mapping(IO &IO, MetadataSection &Section, | |
| MetadataSection::Option &OptionKind) { | |
| IO.mapRequired("targets", Section.Targets); | |
| switch (OptionKind) { | |
| case MetadataSection::Option::Clients: | |
| IO.mapRequired("clients", Section.Values); | |
| return; | |
| case MetadataSection::Option::Libraries: | |
| IO.mapRequired("libraries", Section.Values); | |
| return; | |
| } | |
| llvm_unreachable("unexpected option for metadata"); | |
| } | |
| }; | |
| template <> struct ScalarBitSetTraits<TBDFlags> { | |
| static void bitset(IO &IO, TBDFlags &Flags) { | |
| IO.bitSetCase(Flags, "flat_namespace", TBDFlags::FlatNamespace); | |
| IO.bitSetCase(Flags, "not_app_extension_safe", | |
| TBDFlags::NotApplicationExtensionSafe); | |
| IO.bitSetCase(Flags, "installapi", TBDFlags::InstallAPI); | |
| } | |
| }; | |
| template <> struct ScalarTraits<Target> { | |
| static void output(const Target &Value, void *, raw_ostream &OS) { | |
| OS << Value.Arch << "-"; | |
| switch (Value.Platform) { | |
| default: | |
| OS << "unknown"; | |
| break; | |
| case PLATFORM_MACOS: | |
| OS << "macos"; | |
| break; | |
| case PLATFORM_IOS: | |
| OS << "ios"; | |
| break; | |
| case PLATFORM_TVOS: | |
| OS << "tvos"; | |
| break; | |
| case PLATFORM_WATCHOS: | |
| OS << "watchos"; | |
| break; | |
| case PLATFORM_BRIDGEOS: | |
| OS << "bridgeos"; | |
| break; | |
| case PLATFORM_MACCATALYST: | |
| OS << "maccatalyst"; | |
| break; | |
| case PLATFORM_IOSSIMULATOR: | |
| OS << "ios-simulator"; | |
| break; | |
| case PLATFORM_TVOSSIMULATOR: | |
| OS << "tvos-simulator"; | |
| break; | |
| case PLATFORM_WATCHOSSIMULATOR: | |
| OS << "watchos-simulator"; | |
| break; | |
| case PLATFORM_DRIVERKIT: | |
| OS << "driverkit"; | |
| break; | |
| } | |
| } | |
| static StringRef input(StringRef Scalar, void *, Target &Value) { | |
| auto Result = Target::create(Scalar); | |
| if (!Result) { | |
| consumeError(Result.takeError()); | |
| return "unparsable target"; | |
| } | |
| Value = *Result; | |
| if (Value.Arch == AK_unknown) | |
| return "unknown architecture"; | |
| if (Value.Platform == PLATFORM_UNKNOWN) | |
| return "unknown platform"; | |
| return {}; | |
| } | |
| static QuotingType mustQuote(StringRef) { return QuotingType::None; } | |
| }; | |
| template <> struct MappingTraits<const InterfaceFile *> { | |
| struct NormalizedTBD { | |
| explicit NormalizedTBD(IO &IO) {} | |
| NormalizedTBD(IO &IO, const InterfaceFile *&File) { | |
| Architectures = File->getArchitectures(); | |
| UUIDs = File->uuids(); | |
| Platforms = File->getPlatforms(); | |
| InstallName = File->getInstallName(); | |
| CurrentVersion = PackedVersion(File->getCurrentVersion()); | |
| CompatibilityVersion = PackedVersion(File->getCompatibilityVersion()); | |
| SwiftABIVersion = File->getSwiftABIVersion(); | |
| ObjCConstraint = File->getObjCConstraint(); | |
| Flags = TBDFlags::None; | |
| if (!File->isApplicationExtensionSafe()) | |
| Flags |= TBDFlags::NotApplicationExtensionSafe; | |
| if (!File->isTwoLevelNamespace()) | |
| Flags |= TBDFlags::FlatNamespace; | |
| if (File->isInstallAPI()) | |
| Flags |= TBDFlags::InstallAPI; | |
| if (!File->umbrellas().empty()) | |
| ParentUmbrella = File->umbrellas().begin()->second; | |
| std::set<ArchitectureSet> ArchSet; | |
| for (const auto &Library : File->allowableClients()) | |
| ArchSet.insert(Library.getArchitectures()); | |
| for (const auto &Library : File->reexportedLibraries()) | |
| ArchSet.insert(Library.getArchitectures()); | |
| std::map<const Symbol *, ArchitectureSet> SymbolToArchSet; | |
| for (const auto *Symbol : File->exports()) { | |
| auto Architectures = Symbol->getArchitectures(); | |
| SymbolToArchSet[Symbol] = Architectures; | |
| ArchSet.insert(Architectures); | |
| } | |
| for (auto Architectures : ArchSet) { | |
| ExportSection Section; | |
| Section.Architectures = Architectures; | |
| for (const auto &Library : File->allowableClients()) | |
| if (Library.getArchitectures() == Architectures) | |
| Section.AllowableClients.emplace_back(Library.getInstallName()); | |
| for (const auto &Library : File->reexportedLibraries()) | |
| if (Library.getArchitectures() == Architectures) | |
| Section.ReexportedLibraries.emplace_back(Library.getInstallName()); | |
| for (const auto &SymArch : SymbolToArchSet) { | |
| if (SymArch.second != Architectures) | |
| continue; | |
| const auto *Symbol = SymArch.first; | |
| switch (Symbol->getKind()) { | |
| case SymbolKind::GlobalSymbol: | |
| if (Symbol->isWeakDefined()) | |
| Section.WeakDefSymbols.emplace_back(Symbol->getName()); | |
| else if (Symbol->isThreadLocalValue()) | |
| Section.TLVSymbols.emplace_back(Symbol->getName()); | |
| else | |
| Section.Symbols.emplace_back(Symbol->getName()); | |
| break; | |
| case SymbolKind::ObjectiveCClass: | |
| if (File->getFileType() != FileType::TBD_V3) | |
| Section.Classes.emplace_back( | |
| copyString("_" + Symbol->getName().str())); | |
| else | |
| Section.Classes.emplace_back(Symbol->getName()); | |
| break; | |
| case SymbolKind::ObjectiveCClassEHType: | |
| if (File->getFileType() != FileType::TBD_V3) | |
| Section.Symbols.emplace_back( | |
| copyString("_OBJC_EHTYPE_$_" + Symbol->getName().str())); | |
| else | |
| Section.ClassEHs.emplace_back(Symbol->getName()); | |
| break; | |
| case SymbolKind::ObjectiveCInstanceVariable: | |
| if (File->getFileType() != FileType::TBD_V3) | |
| Section.IVars.emplace_back( | |
| copyString("_" + Symbol->getName().str())); | |
| else | |
| Section.IVars.emplace_back(Symbol->getName()); | |
| break; | |
| } | |
| } | |
| llvm::sort(Section.Symbols); | |
| llvm::sort(Section.Classes); | |
| llvm::sort(Section.ClassEHs); | |
| llvm::sort(Section.IVars); | |
| llvm::sort(Section.WeakDefSymbols); | |
| llvm::sort(Section.TLVSymbols); | |
| Exports.emplace_back(std::move(Section)); | |
| } | |
| ArchSet.clear(); | |
| SymbolToArchSet.clear(); | |
| for (const auto *Symbol : File->undefineds()) { | |
| auto Architectures = Symbol->getArchitectures(); | |
| SymbolToArchSet[Symbol] = Architectures; | |
| ArchSet.insert(Architectures); | |
| } | |
| for (auto Architectures : ArchSet) { | |
| UndefinedSection Section; | |
| Section.Architectures = Architectures; | |
| for (const auto &SymArch : SymbolToArchSet) { | |
| if (SymArch.second != Architectures) | |
| continue; | |
| const auto *Symbol = SymArch.first; | |
| switch (Symbol->getKind()) { | |
| case SymbolKind::GlobalSymbol: | |
| if (Symbol->isWeakReferenced()) | |
| Section.WeakRefSymbols.emplace_back(Symbol->getName()); | |
| else | |
| Section.Symbols.emplace_back(Symbol->getName()); | |
| break; | |
| case SymbolKind::ObjectiveCClass: | |
| if (File->getFileType() != FileType::TBD_V3) | |
| Section.Classes.emplace_back( | |
| copyString("_" + Symbol->getName().str())); | |
| else | |
| Section.Classes.emplace_back(Symbol->getName()); | |
| break; | |
| case SymbolKind::ObjectiveCClassEHType: | |
| if (File->getFileType() != FileType::TBD_V3) | |
| Section.Symbols.emplace_back( | |
| copyString("_OBJC_EHTYPE_$_" + Symbol->getName().str())); | |
| else | |
| Section.ClassEHs.emplace_back(Symbol->getName()); | |
| break; | |
| case SymbolKind::ObjectiveCInstanceVariable: | |
| if (File->getFileType() != FileType::TBD_V3) | |
| Section.IVars.emplace_back( | |
| copyString("_" + Symbol->getName().str())); | |
| else | |
| Section.IVars.emplace_back(Symbol->getName()); | |
| break; | |
| } | |
| } | |
| llvm::sort(Section.Symbols); | |
| llvm::sort(Section.Classes); | |
| llvm::sort(Section.ClassEHs); | |
| llvm::sort(Section.IVars); | |
| llvm::sort(Section.WeakRefSymbols); | |
| Undefineds.emplace_back(std::move(Section)); | |
| } | |
| } | |
| // TBD v1 - TBD v3 files only support one platform and several | |
| // architectures. It is possible to have more than one platform for TBD v3 | |
| // files, but the architectures don't apply to all | |
| // platforms, specifically to filter out the i386 slice from | |
| // platform macCatalyst. | |
| TargetList synthesizeTargets(ArchitectureSet Architectures, | |
| const PlatformSet &Platforms) { | |
| TargetList Targets; | |
| for (auto Platform : Platforms) { | |
| Platform = mapToPlatformType(Platform, Architectures.hasX86()); | |
| for (const auto &&Architecture : Architectures) { | |
| if ((Architecture == AK_i386) && (Platform == PLATFORM_MACCATALYST)) | |
| continue; | |
| Targets.emplace_back(Architecture, Platform); | |
| } | |
| } | |
| return Targets; | |
| } | |
| const InterfaceFile *denormalize(IO &IO) { | |
| auto Ctx = reinterpret_cast<TextAPIContext *>(IO.getContext()); | |
| assert(Ctx); | |
| auto *File = new InterfaceFile; | |
| File->setPath(Ctx->Path); | |
| File->setFileType(Ctx->FileKind); | |
| File->addTargets(synthesizeTargets(Architectures, Platforms)); | |
| for (auto &ID : UUIDs) | |
| File->addUUID(ID.first, ID.second); | |
| File->setInstallName(InstallName); | |
| File->setCurrentVersion(CurrentVersion); | |
| File->setCompatibilityVersion(CompatibilityVersion); | |
| File->setSwiftABIVersion(SwiftABIVersion); | |
| File->setObjCConstraint(ObjCConstraint); | |
| for (const auto &Target : File->targets()) | |
| File->addParentUmbrella(Target, ParentUmbrella); | |
| if (Ctx->FileKind == FileType::TBD_V1) { | |
| File->setTwoLevelNamespace(); | |
| File->setApplicationExtensionSafe(); | |
| } else { | |
| File->setTwoLevelNamespace(!(Flags & TBDFlags::FlatNamespace)); | |
| File->setApplicationExtensionSafe( | |
| !(Flags & TBDFlags::NotApplicationExtensionSafe)); | |
| File->setInstallAPI(Flags & TBDFlags::InstallAPI); | |
| } | |
| for (const auto &Section : Exports) { | |
| const auto Targets = | |
| synthesizeTargets(Section.Architectures, Platforms); | |
| for (const auto &Lib : Section.AllowableClients) | |
| for (const auto &Target : Targets) | |
| File->addAllowableClient(Lib, Target); | |
| for (const auto &Lib : Section.ReexportedLibraries) | |
| for (const auto &Target : Targets) | |
| File->addReexportedLibrary(Lib, Target); | |
| for (const auto &Symbol : Section.Symbols) { | |
| if (Ctx->FileKind != FileType::TBD_V3 && | |
| Symbol.value.startswith("_OBJC_EHTYPE_$_")) | |
| File->addSymbol(SymbolKind::ObjectiveCClassEHType, | |
| Symbol.value.drop_front(15), Targets); | |
| else | |
| File->addSymbol(SymbolKind::GlobalSymbol, Symbol, Targets); | |
| } | |
| for (auto &Symbol : Section.Classes) { | |
| auto Name = Symbol.value; | |
| if (Ctx->FileKind != FileType::TBD_V3) | |
| Name = Name.drop_front(); | |
| File->addSymbol(SymbolKind::ObjectiveCClass, Name, Targets); | |
| } | |
| for (auto &Symbol : Section.ClassEHs) | |
| File->addSymbol(SymbolKind::ObjectiveCClassEHType, Symbol, Targets); | |
| for (auto &Symbol : Section.IVars) { | |
| auto Name = Symbol.value; | |
| if (Ctx->FileKind != FileType::TBD_V3) | |
| Name = Name.drop_front(); | |
| File->addSymbol(SymbolKind::ObjectiveCInstanceVariable, Name, | |
| Targets); | |
| } | |
| for (auto &Symbol : Section.WeakDefSymbols) | |
| File->addSymbol(SymbolKind::GlobalSymbol, Symbol, Targets, | |
| SymbolFlags::WeakDefined); | |
| for (auto &Symbol : Section.TLVSymbols) | |
| File->addSymbol(SymbolKind::GlobalSymbol, Symbol, Targets, | |
| SymbolFlags::ThreadLocalValue); | |
| } | |
| for (const auto &Section : Undefineds) { | |
| const auto Targets = | |
| synthesizeTargets(Section.Architectures, Platforms); | |
| for (auto &Symbol : Section.Symbols) { | |
| if (Ctx->FileKind != FileType::TBD_V3 && | |
| Symbol.value.startswith("_OBJC_EHTYPE_$_")) | |
| File->addSymbol(SymbolKind::ObjectiveCClassEHType, | |
| Symbol.value.drop_front(15), Targets, | |
| SymbolFlags::Undefined); | |
| else | |
| File->addSymbol(SymbolKind::GlobalSymbol, Symbol, Targets, | |
| SymbolFlags::Undefined); | |
| } | |
| for (auto &Symbol : Section.Classes) { | |
| auto Name = Symbol.value; | |
| if (Ctx->FileKind != FileType::TBD_V3) | |
| Name = Name.drop_front(); | |
| File->addSymbol(SymbolKind::ObjectiveCClass, Name, Targets, | |
| SymbolFlags::Undefined); | |
| } | |
| for (auto &Symbol : Section.ClassEHs) | |
| File->addSymbol(SymbolKind::ObjectiveCClassEHType, Symbol, Targets, | |
| SymbolFlags::Undefined); | |
| for (auto &Symbol : Section.IVars) { | |
| auto Name = Symbol.value; | |
| if (Ctx->FileKind != FileType::TBD_V3) | |
| Name = Name.drop_front(); | |
| File->addSymbol(SymbolKind::ObjectiveCInstanceVariable, Name, Targets, | |
| SymbolFlags::Undefined); | |
| } | |
| for (auto &Symbol : Section.WeakRefSymbols) | |
| File->addSymbol(SymbolKind::GlobalSymbol, Symbol, Targets, | |
| SymbolFlags::Undefined | SymbolFlags::WeakReferenced); | |
| } | |
| return File; | |
| } | |
| llvm::BumpPtrAllocator Allocator; | |
| StringRef copyString(StringRef String) { | |
| if (String.empty()) | |
| return {}; | |
| void *Ptr = Allocator.Allocate(String.size(), 1); | |
| memcpy(Ptr, String.data(), String.size()); | |
| return StringRef(reinterpret_cast<const char *>(Ptr), String.size()); | |
| } | |
| std::vector<Architecture> Architectures; | |
| std::vector<UUID> UUIDs; | |
| PlatformSet Platforms; | |
| StringRef InstallName; | |
| PackedVersion CurrentVersion; | |
| PackedVersion CompatibilityVersion; | |
| SwiftVersion SwiftABIVersion{0}; | |
| ObjCConstraintType ObjCConstraint{ObjCConstraintType::None}; | |
| TBDFlags Flags{TBDFlags::None}; | |
| StringRef ParentUmbrella; | |
| std::vector<ExportSection> Exports; | |
| std::vector<UndefinedSection> Undefineds; | |
| }; | |
| static void setFileTypeForInput(TextAPIContext *Ctx, IO &IO) { | |
| if (IO.mapTag("!tapi-tbd", false)) | |
| Ctx->FileKind = FileType::TBD_V4; | |
| else if (IO.mapTag("!tapi-tbd-v3", false)) | |
| Ctx->FileKind = FileType::TBD_V3; | |
| else if (IO.mapTag("!tapi-tbd-v2", false)) | |
| Ctx->FileKind = FileType::TBD_V2; | |
| else if (IO.mapTag("!tapi-tbd-v1", false) || | |
| IO.mapTag("tag:yaml.org,2002:map", false)) | |
| Ctx->FileKind = FileType::TBD_V1; | |
| else { | |
| Ctx->FileKind = FileType::Invalid; | |
| return; | |
| } | |
| } | |
| static void mapping(IO &IO, const InterfaceFile *&File) { | |
| auto *Ctx = reinterpret_cast<TextAPIContext *>(IO.getContext()); | |
| assert((!Ctx || !IO.outputting() || | |
| (Ctx && Ctx->FileKind != FileType::Invalid)) && | |
| "File type is not set in YAML context"); | |
| if (!IO.outputting()) { | |
| setFileTypeForInput(Ctx, IO); | |
| switch (Ctx->FileKind) { | |
| default: | |
| break; | |
| case FileType::TBD_V4: | |
| mapKeysToValuesV4(IO, File); | |
| return; | |
| case FileType::Invalid: | |
| IO.setError("unsupported file type"); | |
| return; | |
| } | |
| } else { | |
| // Set file type when writing. | |
| switch (Ctx->FileKind) { | |
| default: | |
| llvm_unreachable("unexpected file type"); | |
| case FileType::TBD_V4: | |
| mapKeysToValuesV4(IO, File); | |
| return; | |
| case FileType::TBD_V3: | |
| IO.mapTag("!tapi-tbd-v3", true); | |
| break; | |
| case FileType::TBD_V2: | |
| IO.mapTag("!tapi-tbd-v2", true); | |
| break; | |
| case FileType::TBD_V1: | |
| // Don't write the tag into the .tbd file for TBD v1 | |
| break; | |
| } | |
| } | |
| mapKeysToValues(Ctx->FileKind, IO, File); | |
| } | |
| using SectionList = std::vector<SymbolSection>; | |
| struct NormalizedTBD_V4 { | |
| explicit NormalizedTBD_V4(IO &IO) {} | |
| NormalizedTBD_V4(IO &IO, const InterfaceFile *&File) { | |
| auto Ctx = reinterpret_cast<TextAPIContext *>(IO.getContext()); | |
| assert(Ctx); | |
| TBDVersion = Ctx->FileKind >> 1; | |
| Targets.insert(Targets.begin(), File->targets().begin(), | |
| File->targets().end()); | |
| for (const auto &IT : File->uuids()) | |
| UUIDs.emplace_back(IT.first, IT.second); | |
| InstallName = File->getInstallName(); | |
| CurrentVersion = File->getCurrentVersion(); | |
| CompatibilityVersion = File->getCompatibilityVersion(); | |
| SwiftABIVersion = File->getSwiftABIVersion(); | |
| Flags = TBDFlags::None; | |
| if (!File->isApplicationExtensionSafe()) | |
| Flags |= TBDFlags::NotApplicationExtensionSafe; | |
| if (!File->isTwoLevelNamespace()) | |
| Flags |= TBDFlags::FlatNamespace; | |
| if (File->isInstallAPI()) | |
| Flags |= TBDFlags::InstallAPI; | |
| { | |
| std::map<std::string, TargetList> valueToTargetList; | |
| for (const auto &it : File->umbrellas()) | |
| valueToTargetList[it.second].emplace_back(it.first); | |
| for (const auto &it : valueToTargetList) { | |
| UmbrellaSection CurrentSection; | |
| CurrentSection.Targets.insert(CurrentSection.Targets.begin(), | |
| it.second.begin(), it.second.end()); | |
| CurrentSection.Umbrella = it.first; | |
| ParentUmbrellas.emplace_back(std::move(CurrentSection)); | |
| } | |
| } | |
| assignTargetsToLibrary(File->allowableClients(), AllowableClients); | |
| assignTargetsToLibrary(File->reexportedLibraries(), ReexportedLibraries); | |
| auto handleSymbols = | |
| [](SectionList &CurrentSections, | |
| InterfaceFile::const_filtered_symbol_range Symbols, | |
| std::function<bool(const Symbol *)> Pred) { | |
| std::set<TargetList> TargetSet; | |
| std::map<const Symbol *, TargetList> SymbolToTargetList; | |
| for (const auto *Symbol : Symbols) { | |
| if (!Pred(Symbol)) | |
| continue; | |
| TargetList Targets(Symbol->targets()); | |
| SymbolToTargetList[Symbol] = Targets; | |
| TargetSet.emplace(std::move(Targets)); | |
| } | |
| for (const auto &TargetIDs : TargetSet) { | |
| SymbolSection CurrentSection; | |
| CurrentSection.Targets.insert(CurrentSection.Targets.begin(), | |
| TargetIDs.begin(), TargetIDs.end()); | |
| for (const auto &IT : SymbolToTargetList) { | |
| if (IT.second != TargetIDs) | |
| continue; | |
| const auto *Symbol = IT.first; | |
| switch (Symbol->getKind()) { | |
| case SymbolKind::GlobalSymbol: | |
| if (Symbol->isWeakDefined()) | |
| CurrentSection.WeakSymbols.emplace_back(Symbol->getName()); | |
| else if (Symbol->isThreadLocalValue()) | |
| CurrentSection.TlvSymbols.emplace_back(Symbol->getName()); | |
| else | |
| CurrentSection.Symbols.emplace_back(Symbol->getName()); | |
| break; | |
| case SymbolKind::ObjectiveCClass: | |
| CurrentSection.Classes.emplace_back(Symbol->getName()); | |
| break; | |
| case SymbolKind::ObjectiveCClassEHType: | |
| CurrentSection.ClassEHs.emplace_back(Symbol->getName()); | |
| break; | |
| case SymbolKind::ObjectiveCInstanceVariable: | |
| CurrentSection.Ivars.emplace_back(Symbol->getName()); | |
| break; | |
| } | |
| } | |
| sort(CurrentSection.Symbols); | |
| sort(CurrentSection.Classes); | |
| sort(CurrentSection.ClassEHs); | |
| sort(CurrentSection.Ivars); | |
| sort(CurrentSection.WeakSymbols); | |
| sort(CurrentSection.TlvSymbols); | |
| CurrentSections.emplace_back(std::move(CurrentSection)); | |
| } | |
| }; | |
| handleSymbols(Exports, File->exports(), [](const Symbol *Symbol) { | |
| return !Symbol->isReexported(); | |
| }); | |
| handleSymbols(Reexports, File->exports(), [](const Symbol *Symbol) { | |
| return Symbol->isReexported(); | |
| }); | |
| handleSymbols(Undefineds, File->undefineds(), | |
| [](const Symbol *Symbol) { return true; }); | |
| } | |
| const InterfaceFile *denormalize(IO &IO) { | |
| auto Ctx = reinterpret_cast<TextAPIContext *>(IO.getContext()); | |
| assert(Ctx); | |
| auto *File = new InterfaceFile; | |
| File->setPath(Ctx->Path); | |
| File->setFileType(Ctx->FileKind); | |
| for (auto &id : UUIDs) | |
| File->addUUID(id.TargetID, id.Value); | |
| File->addTargets(Targets); | |
| File->setInstallName(InstallName); | |
| File->setCurrentVersion(CurrentVersion); | |
| File->setCompatibilityVersion(CompatibilityVersion); | |
| File->setSwiftABIVersion(SwiftABIVersion); | |
| for (const auto &CurrentSection : ParentUmbrellas) | |
| for (const auto &target : CurrentSection.Targets) | |
| File->addParentUmbrella(target, CurrentSection.Umbrella); | |
| File->setTwoLevelNamespace(!(Flags & TBDFlags::FlatNamespace)); | |
| File->setApplicationExtensionSafe( | |
| !(Flags & TBDFlags::NotApplicationExtensionSafe)); | |
| File->setInstallAPI(Flags & TBDFlags::InstallAPI); | |
| for (const auto &CurrentSection : AllowableClients) { | |
| for (const auto &lib : CurrentSection.Values) | |
| for (const auto &Target : CurrentSection.Targets) | |
| File->addAllowableClient(lib, Target); | |
| } | |
| for (const auto &CurrentSection : ReexportedLibraries) { | |
| for (const auto &Lib : CurrentSection.Values) | |
| for (const auto &Target : CurrentSection.Targets) | |
| File->addReexportedLibrary(Lib, Target); | |
| } | |
| auto handleSymbols = [File](const SectionList &CurrentSections, | |
| SymbolFlags Flag = SymbolFlags::None) { | |
| for (const auto &CurrentSection : CurrentSections) { | |
| for (auto &sym : CurrentSection.Symbols) | |
| File->addSymbol(SymbolKind::GlobalSymbol, sym, | |
| CurrentSection.Targets, Flag); | |
| for (auto &sym : CurrentSection.Classes) | |
| File->addSymbol(SymbolKind::ObjectiveCClass, sym, | |
| CurrentSection.Targets); | |
| for (auto &sym : CurrentSection.ClassEHs) | |
| File->addSymbol(SymbolKind::ObjectiveCClassEHType, sym, | |
| CurrentSection.Targets); | |
| for (auto &sym : CurrentSection.Ivars) | |
| File->addSymbol(SymbolKind::ObjectiveCInstanceVariable, sym, | |
| CurrentSection.Targets); | |
| for (auto &sym : CurrentSection.WeakSymbols) | |
| File->addSymbol(SymbolKind::GlobalSymbol, sym, | |
| CurrentSection.Targets, SymbolFlags::WeakDefined); | |
| for (auto &sym : CurrentSection.TlvSymbols) | |
| File->addSymbol(SymbolKind::GlobalSymbol, sym, | |
| CurrentSection.Targets, | |
| SymbolFlags::ThreadLocalValue); | |
| } | |
| }; | |
| handleSymbols(Exports); | |
| handleSymbols(Reexports, SymbolFlags::Rexported); | |
| handleSymbols(Undefineds, SymbolFlags::Undefined); | |
| return File; | |
| } | |
| unsigned TBDVersion; | |
| std::vector<UUIDv4> UUIDs; | |
| TargetList Targets; | |
| StringRef InstallName; | |
| PackedVersion CurrentVersion; | |
| PackedVersion CompatibilityVersion; | |
| SwiftVersion SwiftABIVersion{0}; | |
| std::vector<MetadataSection> AllowableClients; | |
| std::vector<MetadataSection> ReexportedLibraries; | |
| TBDFlags Flags{TBDFlags::None}; | |
| std::vector<UmbrellaSection> ParentUmbrellas; | |
| SectionList Exports; | |
| SectionList Reexports; | |
| SectionList Undefineds; | |
| private: | |
| void assignTargetsToLibrary(const std::vector<InterfaceFileRef> &Libraries, | |
| std::vector<MetadataSection> &Section) { | |
| std::set<TargetList> targetSet; | |
| std::map<const InterfaceFileRef *, TargetList> valueToTargetList; | |
| for (const auto &library : Libraries) { | |
| TargetList targets(library.targets()); | |
| valueToTargetList[&library] = targets; | |
| targetSet.emplace(std::move(targets)); | |
| } | |
| for (const auto &targets : targetSet) { | |
| MetadataSection CurrentSection; | |
| CurrentSection.Targets.insert(CurrentSection.Targets.begin(), | |
| targets.begin(), targets.end()); | |
| for (const auto &it : valueToTargetList) { | |
| if (it.second != targets) | |
| continue; | |
| CurrentSection.Values.emplace_back(it.first->getInstallName()); | |
| } | |
| llvm::sort(CurrentSection.Values); | |
| Section.emplace_back(std::move(CurrentSection)); | |
| } | |
| } | |
| }; | |
| static void mapKeysToValues(FileType FileKind, IO &IO, | |
| const InterfaceFile *&File) { | |
| MappingNormalization<NormalizedTBD, const InterfaceFile *> Keys(IO, File); | |
| IO.mapRequired("archs", Keys->Architectures); | |
| if (FileKind != FileType::TBD_V1) | |
| IO.mapOptional("uuids", Keys->UUIDs); | |
| IO.mapRequired("platform", Keys->Platforms); | |
| if (FileKind != FileType::TBD_V1) | |
| IO.mapOptional("flags", Keys->Flags, TBDFlags::None); | |
| IO.mapRequired("install-name", Keys->InstallName); | |
| IO.mapOptional("current-version", Keys->CurrentVersion, | |
| PackedVersion(1, 0, 0)); | |
| IO.mapOptional("compatibility-version", Keys->CompatibilityVersion, | |
| PackedVersion(1, 0, 0)); | |
| if (FileKind != FileType::TBD_V3) | |
| IO.mapOptional("swift-version", Keys->SwiftABIVersion, SwiftVersion(0)); | |
| else | |
| IO.mapOptional("swift-abi-version", Keys->SwiftABIVersion, | |
| SwiftVersion(0)); | |
| IO.mapOptional("objc-constraint", Keys->ObjCConstraint, | |
| (FileKind == FileType::TBD_V1) | |
| ? ObjCConstraintType::None | |
| : ObjCConstraintType::Retain_Release); | |
| if (FileKind != FileType::TBD_V1) | |
| IO.mapOptional("parent-umbrella", Keys->ParentUmbrella, StringRef()); | |
| IO.mapOptional("exports", Keys->Exports); | |
| if (FileKind != FileType::TBD_V1) | |
| IO.mapOptional("undefineds", Keys->Undefineds); | |
| } | |
| static void mapKeysToValuesV4(IO &IO, const InterfaceFile *&File) { | |
| MappingNormalization<NormalizedTBD_V4, const InterfaceFile *> Keys(IO, | |
| File); | |
| IO.mapTag("!tapi-tbd", true); | |
| IO.mapRequired("tbd-version", Keys->TBDVersion); | |
| IO.mapRequired("targets", Keys->Targets); | |
| IO.mapOptional("uuids", Keys->UUIDs); | |
| IO.mapOptional("flags", Keys->Flags, TBDFlags::None); | |
| IO.mapRequired("install-name", Keys->InstallName); | |
| IO.mapOptional("current-version", Keys->CurrentVersion, | |
| PackedVersion(1, 0, 0)); | |
| IO.mapOptional("compatibility-version", Keys->CompatibilityVersion, | |
| PackedVersion(1, 0, 0)); | |
| IO.mapOptional("swift-abi-version", Keys->SwiftABIVersion, SwiftVersion(0)); | |
| IO.mapOptional("parent-umbrella", Keys->ParentUmbrellas); | |
| auto OptionKind = MetadataSection::Option::Clients; | |
| IO.mapOptionalWithContext("allowable-clients", Keys->AllowableClients, | |
| OptionKind); | |
| OptionKind = MetadataSection::Option::Libraries; | |
| IO.mapOptionalWithContext("reexported-libraries", Keys->ReexportedLibraries, | |
| OptionKind); | |
| IO.mapOptional("exports", Keys->Exports); | |
| IO.mapOptional("reexports", Keys->Reexports); | |
| IO.mapOptional("undefineds", Keys->Undefineds); | |
| } | |
| }; | |
| template <> | |
| struct DocumentListTraits<std::vector<const MachO::InterfaceFile *>> { | |
| static size_t size(IO &IO, std::vector<const MachO::InterfaceFile *> &Seq) { | |
| return Seq.size(); | |
| } | |
| static const InterfaceFile *& | |
| element(IO &IO, std::vector<const InterfaceFile *> &Seq, size_t Index) { | |
| if (Index >= Seq.size()) | |
| Seq.resize(Index + 1); | |
| return Seq[Index]; | |
| } | |
| }; | |
| } // end namespace yaml. | |
| } // namespace llvm | |
| static void DiagHandler(const SMDiagnostic &Diag, void *Context) { | |
| auto *File = static_cast<TextAPIContext *>(Context); | |
| SmallString<1024> Message; | |
| raw_svector_ostream S(Message); | |
| SMDiagnostic NewDiag(*Diag.getSourceMgr(), Diag.getLoc(), File->Path, | |
| Diag.getLineNo(), Diag.getColumnNo(), Diag.getKind(), | |
| Diag.getMessage(), Diag.getLineContents(), | |
| Diag.getRanges(), Diag.getFixIts()); | |
| NewDiag.print(nullptr, S); | |
| File->ErrorMessage = ("malformed file\n" + Message).str(); | |
| } | |
| Expected<std::unique_ptr<InterfaceFile>> | |
| TextAPIReader::get(MemoryBufferRef InputBuffer) { | |
| TextAPIContext Ctx; | |
| Ctx.Path = std::string(InputBuffer.getBufferIdentifier()); | |
| yaml::Input YAMLIn(InputBuffer.getBuffer(), &Ctx, DiagHandler, &Ctx); | |
| // Fill vector with interface file objects created by parsing the YAML file. | |
| std::vector<const InterfaceFile *> Files; | |
| YAMLIn >> Files; | |
| // YAMLIn dynamically allocates for Interface file and in case of error, | |
| // memory leak will occur unless wrapped around unique_ptr | |
| auto File = std::unique_ptr<InterfaceFile>( | |
| const_cast<InterfaceFile *>(Files.front())); | |
| for (const InterfaceFile *FI : llvm::drop_begin(Files)) | |
| File->addDocument( | |
| std::shared_ptr<InterfaceFile>(const_cast<InterfaceFile *>(FI))); | |
| if (YAMLIn.error()) | |
| return make_error<StringError>(Ctx.ErrorMessage, YAMLIn.error()); | |
| return std::move(File); | |
| } | |
| Error TextAPIWriter::writeToStream(raw_ostream &OS, const InterfaceFile &File) { | |
| TextAPIContext Ctx; | |
| Ctx.Path = std::string(File.getPath()); | |
| Ctx.FileKind = File.getFileType(); | |
| llvm::yaml::Output YAMLOut(OS, &Ctx, /*WrapColumn=*/80); | |
| std::vector<const InterfaceFile *> Files; | |
| Files.emplace_back(&File); | |
| for (auto Document : File.documents()) | |
| Files.emplace_back(Document.get()); | |
| // Stream out yaml. | |
| YAMLOut << Files; | |
| return Error::success(); | |
| } |