|
9 | 9 | // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
10 | 10 | // |
11 | 11 | //===----------------------------------------------------------------------===// |
12 | | -// NOTE: This basic plugin mechanism is mostly copied from |
13 | | -// https://github.com/apple/swift-package-manager/blob/main/Sources/PackagePlugin/Plugin.swift |
14 | 12 |
|
15 | 13 | #if swift(>=6.0) |
16 | | -private import _SwiftSyntaxCShims |
17 | 14 | public import SwiftSyntaxMacros |
18 | 15 | @_spi(PluginMessage) private import SwiftCompilerPluginMessageHandling |
19 | | -#if canImport(Darwin) |
20 | | -private import Darwin |
21 | | -#elseif canImport(Glibc) |
22 | | -private import Glibc |
23 | | -#elseif canImport(ucrt) |
24 | | -private import ucrt |
25 | | -#endif |
26 | 16 | #else |
27 | | -import _SwiftSyntaxCShims |
28 | 17 | import SwiftSyntaxMacros |
29 | 18 | @_spi(PluginMessage) import SwiftCompilerPluginMessageHandling |
30 | | -#if canImport(Darwin) |
31 | | -import Darwin |
32 | | -#elseif canImport(Glibc) |
33 | | -import Glibc |
34 | | -#elseif canImport(ucrt) |
35 | | -import ucrt |
36 | | -#endif |
37 | 19 | #endif |
38 | 20 |
|
39 | 21 | // |
@@ -117,163 +99,20 @@ struct MacroProviderAdapter<Plugin: CompilerPlugin>: PluginProvider { |
117 | 99 | } |
118 | 100 | } |
119 | 101 |
|
120 | | -#if canImport(ucrt) |
121 | | -private let dup = _dup(_:) |
122 | | -private let fileno = _fileno(_:) |
123 | | -private let dup2 = _dup2(_:_:) |
124 | | -private let close = _close(_:) |
125 | | -private let read = _read(_:_:_:) |
126 | | -private let write = _write(_:_:_:) |
127 | | -#endif |
128 | | - |
129 | 102 | extension CompilerPlugin { |
130 | 103 |
|
131 | | - /// Main entry point of the plugin — sets up a communication channel with |
132 | | - /// the plugin host and runs the main message loop. |
| 104 | + /// Main entry point of the plugin — sets up a standard I/O communication |
| 105 | + /// channel with the plugin host and runs the main message loop. |
133 | 106 | public static func main() throws { |
134 | | - // Duplicate the `stdin` file descriptor, which we will then use for |
135 | | - // receiving messages from the plugin host. |
136 | | - let inputFD = dup(fileno(_stdin)) |
137 | | - guard inputFD >= 0 else { |
138 | | - internalError("Could not duplicate `stdin`: \(describe(errno: _errno)).") |
139 | | - } |
140 | | - |
141 | | - // Having duplicated the original standard-input descriptor, we close |
142 | | - // `stdin` so that attempts by the plugin to read console input (which |
143 | | - // are usually a mistake) return errors instead of blocking. |
144 | | - guard close(fileno(_stdin)) >= 0 else { |
145 | | - internalError("Could not close `stdin`: \(describe(errno: _errno)).") |
146 | | - } |
147 | | - |
148 | | - // Duplicate the `stdout` file descriptor, which we will then use for |
149 | | - // sending messages to the plugin host. |
150 | | - let outputFD = dup(fileno(_stdout)) |
151 | | - guard outputFD >= 0 else { |
152 | | - internalError("Could not dup `stdout`: \(describe(errno: _errno)).") |
153 | | - } |
154 | | - |
155 | | - // Having duplicated the original standard-output descriptor, redirect |
156 | | - // `stdout` to `stderr` so that all free-form text output goes there. |
157 | | - guard dup2(fileno(_stderr), fileno(_stdout)) >= 0 else { |
158 | | - internalError("Could not dup2 `stdout` to `stderr`: \(describe(errno: _errno)).") |
159 | | - } |
160 | | - |
161 | | - #if canImport(ucrt) |
162 | | - // Set I/O to binary mode. Avoid CRLF translation, and Ctrl+Z (0x1A) as EOF. |
163 | | - _ = _setmode(inputFD, _O_BINARY) |
164 | | - _ = _setmode(outputFD, _O_BINARY) |
165 | | - #endif |
166 | | - |
167 | | - // Open a message channel for communicating with the plugin host. |
168 | | - let connection = PluginHostConnection( |
169 | | - inputStream: inputFD, |
170 | | - outputStream: outputFD |
171 | | - ) |
172 | | - |
173 | | - // Handle messages from the host until the input stream is closed, |
174 | | - // indicating that we're done. |
| 107 | + let connection = try StandardIOMessageConnection() |
175 | 108 | let provider = MacroProviderAdapter(plugin: Self()) |
176 | 109 | let impl = CompilerPluginMessageHandler(connection: connection, provider: provider) |
177 | 110 | do { |
178 | 111 | try impl.main() |
179 | 112 | } catch { |
180 | 113 | // Emit a diagnostic and indicate failure to the plugin host, |
181 | 114 | // and exit with an error code. |
182 | | - internalError(String(describing: error)) |
183 | | - } |
184 | | - } |
185 | | - |
186 | | - // Private function to report internal errors and then exit. |
187 | | - fileprivate static func internalError(_ message: String) -> Never { |
188 | | - fputs("Internal Error: \(message)\n", _stderr) |
189 | | - exit(1) |
190 | | - } |
191 | | -} |
192 | | - |
193 | | -internal struct PluginHostConnection: MessageConnection { |
194 | | - // File descriptor for input from the host. |
195 | | - fileprivate let inputStream: CInt |
196 | | - // File descriptor for output to the host. |
197 | | - fileprivate let outputStream: CInt |
198 | | - |
199 | | - func sendMessage<TX: Encodable>(_ message: TX) throws { |
200 | | - // Encode the message as JSON. |
201 | | - let payload = try JSON.encode(message) |
202 | | - |
203 | | - // Write the header (a 64-bit length field in little endian byte order). |
204 | | - let count = payload.count |
205 | | - var header = UInt64(count).littleEndian |
206 | | - try withUnsafeBytes(of: &header) { try _write(outputStream, contentsOf: $0) } |
207 | | - |
208 | | - // Write the JSON payload. |
209 | | - try payload.withUnsafeBytes { try _write(outputStream, contentsOf: $0) } |
210 | | - } |
211 | | - |
212 | | - func waitForNextMessage<RX: Decodable>(_ ty: RX.Type) throws -> RX? { |
213 | | - // Read the header (a 64-bit length field in little endian byte order). |
214 | | - var header: UInt64 = 0 |
215 | | - do { |
216 | | - try withUnsafeMutableBytes(of: &header) { try _read(inputStream, into: $0) } |
217 | | - } catch IOError.readReachedEndOfInput { |
218 | | - // Connection closed. |
219 | | - return nil |
220 | | - } |
221 | | - |
222 | | - // Read the JSON payload. |
223 | | - let count = Int(UInt64(littleEndian: header)) |
224 | | - let data = UnsafeMutableRawBufferPointer.allocate(byteCount: count, alignment: 1) |
225 | | - defer { data.deallocate() } |
226 | | - try _read(inputStream, into: data) |
227 | | - |
228 | | - // Decode and return the message. |
229 | | - return try JSON.decode(ty, from: UnsafeBufferPointer(data.bindMemory(to: UInt8.self))) |
230 | | - } |
231 | | -} |
232 | | - |
233 | | -/// Write the buffer to the file descriptor. Throws an error on failure. |
234 | | -private func _write(_ fd: CInt, contentsOf buffer: UnsafeRawBufferPointer) throws { |
235 | | - guard var ptr = buffer.baseAddress else { return } |
236 | | - let endPtr = ptr.advanced(by: buffer.count) |
237 | | - while ptr != endPtr { |
238 | | - switch write(fd, ptr, numericCast(endPtr - ptr)) { |
239 | | - case -1: throw IOError.writeFailed(errno: _errno) |
240 | | - case 0: throw IOError.writeFailed(errno: 0) /* unreachable */ |
241 | | - case let n: ptr += Int(n) |
| 115 | + fatalError("Internal Error: \(error)") |
242 | 116 | } |
243 | 117 | } |
244 | 118 | } |
245 | | - |
246 | | -/// Fill the buffer from the file descriptor. Throws an error on failure. |
247 | | -/// If the file descriptor reached the end-of-file before filling up the entire |
248 | | -/// buffer, throws IOError.readReachedEndOfInput |
249 | | -private func _read(_ fd: CInt, into buffer: UnsafeMutableRawBufferPointer) throws { |
250 | | - guard var ptr = buffer.baseAddress else { return } |
251 | | - let endPtr = ptr.advanced(by: buffer.count) |
252 | | - while ptr != endPtr { |
253 | | - switch read(fd, ptr, numericCast(endPtr - ptr)) { |
254 | | - case -1: throw IOError.readFailed(errno: _errno) |
255 | | - case 0: throw IOError.readReachedEndOfInput |
256 | | - case let n: ptr += Int(n) |
257 | | - } |
258 | | - } |
259 | | -} |
260 | | - |
261 | | -private enum IOError: Error, CustomStringConvertible { |
262 | | - case readReachedEndOfInput |
263 | | - case readFailed(errno: CInt) |
264 | | - case writeFailed(errno: CInt) |
265 | | - |
266 | | - var description: String { |
267 | | - switch self { |
268 | | - case .readReachedEndOfInput: "read(2) reached end-of-file" |
269 | | - case .readFailed(let errno): "read(2) failed: \(describe(errno: errno))" |
270 | | - case .writeFailed(let errno): "write(2) failed: \(describe(errno: errno))" |
271 | | - } |
272 | | - } |
273 | | -} |
274 | | - |
275 | | -// Private function to construct an error message from an `errno` code. |
276 | | -private func describe(errno: CInt) -> String { |
277 | | - if let cStr = strerror(errno) { return String(cString: cStr) } |
278 | | - return String(describing: errno) |
279 | | -} |
0 commit comments