1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
|
import Foundation
/// A wrapper for the swift-api-digester tool.
struct SwiftAPIDigester {
/// The path to `swift-api-digester` executable in the toolchain
let executableURL: URL
init(executableURL: URL) {
self.executableURL = executableURL
}
struct Output: Decodable {
let ABIRoot: SDKNode
static func parse(_ bytes: Data) throws -> Output {
let decoder = JSONDecoder()
return try decoder.decode(Output.self, from: bytes)
}
}
struct SDKNodeInherit<Parent: Decodable, Body: Decodable>: Decodable {
let parent: Parent
let body: Body
init(from decoder: Decoder) throws {
self.parent = try Parent(from: decoder)
self.body = try Body(from: decoder)
}
}
enum SDKNode: Decodable {
case root(SDKNodeRoot)
case decl(SDKNodeDecl)
case typeDecl(SDKNodeDeclType)
case typeNominal(SDKNodeTypeNominal)
case unknown(SDKNodeBody)
var body: SDKNodeBody {
switch self {
case .root(let node): return node.parent
case .decl(let node): return node.parent
case .typeDecl(let node): return node.parent.parent
case .typeNominal(let node): return node.parent.parent
case .unknown(let node): return node
}
}
var decl: SDKNodeDecl? {
switch self {
case .root: return nil
case .decl(let node): return node
case .typeDecl(let node): return node.parent
case .typeNominal: return nil
case .unknown: return nil
}
}
enum CodingKeys: CodingKey {
case kind
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
switch try container.decode(String.self, forKey: .kind) {
case "Root":
self = try .root(SDKNodeRoot(from: decoder))
case "TypeDecl":
self = try .typeDecl(SDKNodeDeclType(from: decoder))
case "TypeNominal":
self = try .typeNominal(SDKNodeTypeNominal(from: decoder))
case "Var", "Function":
self = try .decl(SDKNodeDecl(from: decoder))
default:
self = try .unknown(SwiftAPIDigester.SDKNodeBody(from: decoder))
}
}
}
struct SDKNodeBody: Decodable {
let kind: String
let name: String
let printedName: String
let children: [SDKNode]?
enum CodingKeys: CodingKey {
case kind
case name
case printedName
case children
}
}
typealias SDKNodeRoot = SDKNodeInherit<SDKNodeBody, SDKNodeRootBody>
struct SDKNodeRootBody: Codable {
let json_format_version: Int
}
struct SDKNodeDeclBody: Codable {
let declKind: String
let usr: String
let mangledName: String
let moduleName: String
let declAttributes: [String]?
let spi_group_names: [String]?
let `static`: Bool?
}
struct SDKNodeDeclTypeBody: Codable {}
struct SDKNodeTypeBody: Codable {}
struct SDKNodeTypeNominalBody: Codable {
let usr: String?
}
typealias SDKNodeDecl = SDKNodeInherit<SDKNodeBody, SDKNodeDeclBody>
typealias SDKNodeDeclType = SDKNodeInherit<SDKNodeDecl, SDKNodeDeclTypeBody>
typealias SDKNodeType = SDKNodeInherit<SDKNodeBody, SDKNodeTypeBody>
typealias SDKNodeTypeNominal = SDKNodeInherit<SDKNodeType, SDKNodeTypeNominalBody>
func dumpSDK(moduleName: String, arguments: [String]) throws -> Output {
var args = [
"-dump-sdk",
"-module", moduleName,
// Emit output to stdout
"-o", "-",
]
args += arguments
let process = Process()
process.executableURL = executableURL
process.arguments = args
let stdoutPipe = Pipe()
process.standardOutput = stdoutPipe
try process.run()
guard let output = try stdoutPipe.fileHandleForReading.readToEnd() else {
throw SwiftAPIDigesterError.unexpectedEmptyOutput
}
process.waitUntilExit()
return try Output.parse(output)
}
}
enum SwiftAPIDigesterError: Error {
case unexpectedEmptyOutput
}
|