|
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 = CompilerPluginMessageListener(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