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