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