1313// https://github.com/apple/swift-package-manager/blob/main/Sources/PackagePlugin/Plugin.swift
1414
1515#if swift(>=6.0)
16+ private import _SwiftSyntaxCShims
1617public import SwiftSyntaxMacros
17- private import Foundation
1818@_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
1926#else
27+ import _SwiftSyntaxCShims
2028import SwiftSyntaxMacros
21- import Foundation
2229@_spi ( PluginMessage) import SwiftCompilerPluginMessageHandling
23- #endif
24-
25- #if os(Windows)
26- #if swift(>=6.0)
27- private import ucrt
28- #else
30+ #if canImport(Darwin)
31+ import Darwin
32+ #elseif canImport(Glibc)
33+ import Glibc
34+ #elseif canImport(ucrt)
2935import ucrt
3036#endif
3137#endif
@@ -86,14 +92,21 @@ extension CompilerPlugin {
8692 }
8793 }
8894
89- let pluginPath = CommandLine . arguments. first ?? Bundle . main . executablePath ?? ProcessInfo . processInfo . processName
95+ let pluginPath = CommandLine . arguments. first ?? " <unknown> "
9096 throw CompilerPluginError (
9197 message:
9298 " macro implementation type ' \( moduleName) . \( typeName) ' could not be found in executable plugin ' \( pluginPath) ' "
9399 )
94100 }
95101}
96102
103+ struct CompilerPluginError : Error , CustomStringConvertible {
104+ var description : String
105+ init ( message: String ) {
106+ self . description = message
107+ }
108+ }
109+
97110struct MacroProviderAdapter < Plugin: CompilerPlugin > : PluginProvider {
98111 let plugin : Plugin
99112 init ( plugin: Plugin ) {
@@ -104,53 +117,61 @@ struct MacroProviderAdapter<Plugin: CompilerPlugin>: PluginProvider {
104117 }
105118}
106119
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+
107129extension CompilerPlugin {
108130
109131 /// Main entry point of the plugin — sets up a communication channel with
110132 /// the plugin host and runs the main message loop.
111133 public static func main( ) throws {
134+ let stdin = _ss_stdin ( )
135+ let stdout = _ss_stdout ( )
136+ let stderr = _ss_stderr ( )
137+
112138 // Duplicate the `stdin` file descriptor, which we will then use for
113139 // receiving messages from the plugin host.
114140 let inputFD = dup ( fileno ( stdin) )
115141 guard inputFD >= 0 else {
116- internalError ( " Could not duplicate `stdin`: \( describe ( errno: errno ) ) . " )
142+ internalError ( " Could not duplicate `stdin`: \( describe ( errno: _ss_errno ( ) ) ) . " )
117143 }
118144
119145 // Having duplicated the original standard-input descriptor, we close
120146 // `stdin` so that attempts by the plugin to read console input (which
121147 // are usually a mistake) return errors instead of blocking.
122148 guard close ( fileno ( stdin) ) >= 0 else {
123- internalError ( " Could not close `stdin`: \( describe ( errno: errno ) ) . " )
149+ internalError ( " Could not close `stdin`: \( describe ( errno: _ss_errno ( ) ) ) . " )
124150 }
125151
126152 // Duplicate the `stdout` file descriptor, which we will then use for
127153 // sending messages to the plugin host.
128154 let outputFD = dup ( fileno ( stdout) )
129155 guard outputFD >= 0 else {
130- internalError ( " Could not dup `stdout`: \( describe ( errno: errno ) ) . " )
156+ internalError ( " Could not dup `stdout`: \( describe ( errno: _ss_errno ( ) ) ) . " )
131157 }
132158
133159 // Having duplicated the original standard-output descriptor, redirect
134160 // `stdout` to `stderr` so that all free-form text output goes there.
135161 guard dup2 ( fileno ( stderr) , fileno ( stdout) ) >= 0 else {
136- internalError ( " Could not dup2 `stdout` to `stderr`: \( describe ( errno: errno ) ) . " )
162+ internalError ( " Could not dup2 `stdout` to `stderr`: \( describe ( errno: _ss_errno ( ) ) ) . " )
137163 }
138164
139- // Turn off full buffering so printed text appears as soon as possible.
140- // Windows is much less forgiving than other platforms. If line
141- // buffering is enabled, we must provide a buffer and the size of the
142- // buffer. As a result, on Windows, we completely disable all
143- // buffering, which means that partial writes are possible.
144- #if os(Windows)
145- setvbuf ( stdout, nil , _IONBF, 0 )
146- #else
147- setvbuf ( stdout, nil , _IOLBF, 0 )
165+ #if canImport(ucrt)
166+ // Set I/O to binary mode. Avoid CRLF translation, and Ctrl+Z (0x1A) as EOF.
167+ _ = _setmode ( inputFD, _O_BINARY)
168+ _ = _setmode ( outputFD, _O_BINARY)
148169 #endif
149170
150171 // Open a message channel for communicating with the plugin host.
151172 let connection = PluginHostConnection (
152- inputStream: FileHandle ( fileDescriptor : inputFD) ,
153- outputStream: FileHandle ( fileDescriptor : outputFD)
173+ inputStream: inputFD,
174+ outputStream: outputFD
154175 )
155176
156177 // Handle messages from the host until the input stream is closed,
@@ -168,95 +189,95 @@ extension CompilerPlugin {
168189
169190 // Private function to report internal errors and then exit.
170191 fileprivate static func internalError( _ message: String ) -> Never {
171- fputs ( " Internal Error: \( message) \n " , stderr )
192+ fputs ( " Internal Error: \( message) \n " , _ss_stderr ( ) )
172193 exit ( 1 )
173194 }
174-
175- // Private function to construct an error message from an `errno` code.
176- fileprivate static func describe( errno: Int32 ) -> String {
177- if let cStr = strerror ( errno) { return String ( cString: cStr) }
178- return String ( describing: errno)
179- }
180195}
181196
182197internal struct PluginHostConnection : MessageConnection {
183- fileprivate let inputStream : FileHandle
184- fileprivate let outputStream : FileHandle
198+ // File descriptor for input from the host.
199+ fileprivate let inputStream : CInt
200+ // File descriptor for output to the host.
201+ fileprivate let outputStream : CInt
185202
186203 func sendMessage< TX: Encodable > ( _ message: TX ) throws {
187204 // Encode the message as JSON.
188- let payload = try JSONEncoder ( ) . encode ( message)
205+ let payload = try JSON . encode ( message)
189206
190207 // Write the header (a 64-bit length field in little endian byte order).
191- var count = UInt64 ( payload. count) . littleEndian
192- let header = Swift . withUnsafeBytes ( of : & count) { Data ( $0 ) }
193- precondition ( header. count == 8 )
208+ let count = payload. count
209+ var header = UInt64 ( count) . littleEndian
210+ try withUnsafeBytes ( of : & header) { try _write ( outputStream , contentsOf : $0 ) }
194211
195- // Write the header and payload.
196- try outputStream. _write ( contentsOf: header)
197- try outputStream. _write ( contentsOf: payload)
212+ // Write the JSON payload.
213+ try payload. withUnsafeBytes { try _write ( outputStream, contentsOf: $0) }
198214 }
199215
200216 func waitForNextMessage< RX: Decodable > ( _ ty: RX . Type ) throws -> RX ? {
201217 // Read the header (a 64-bit length field in little endian byte order).
202- guard
203- let header = try inputStream. _read ( upToCount: 8 ) ,
204- header. count != 0
205- else {
218+ var header : UInt64 = 0
219+ do {
220+ try withUnsafeMutableBytes ( of: & header) { try _read ( inputStream, into: $0) }
221+ } catch IOError . readReachedEndOfInput {
222+ // Connection closed.
206223 return nil
207224 }
208- guard header. count == 8 else {
209- throw PluginMessageError . truncatedHeader
210- }
211-
212- // Decode the count.
213- let count = header. withUnsafeBytes {
214- UInt64 ( littleEndian: $0. loadUnaligned ( as: UInt64 . self) )
215- }
216- guard count >= 2 else {
217- throw PluginMessageError . invalidPayloadSize
218- }
219225
220226 // Read the JSON payload.
221- guard
222- let payload = try inputStream. _read ( upToCount: Int ( count) ) ,
223- payload. count == count
224- else {
225- throw PluginMessageError . truncatedPayload
226- }
227+ let count = Int ( UInt64 ( littleEndian: header) )
228+ let data = UnsafeMutableRawBufferPointer . allocate ( byteCount: count, alignment: 1 )
229+ defer { data. deallocate ( ) }
230+ try _read ( inputStream, into: data)
227231
228232 // Decode and return the message.
229- return try JSONDecoder ( ) . decode ( RX . self , from: payload )
233+ return try JSON . decode ( ty , from: UnsafeBufferPointer ( data . bindMemory ( to : UInt8 . self ) ) )
230234 }
235+ }
231236
232- enum PluginMessageError : Swift . Error {
233- case truncatedHeader
234- case invalidPayloadSize
235- case truncatedPayload
237+ /// Write the buffer to the file descriptor. Throws an error on failure.
238+ private func _write( _ fd: CInt , contentsOf buffer: UnsafeRawBufferPointer ) throws {
239+ guard var ptr = buffer. baseAddress else { return }
240+ let endPtr = ptr. advanced ( by: buffer. count)
241+ while ptr != endPtr {
242+ switch write ( fd, ptr, numericCast ( endPtr - ptr) ) {
243+ case - 1 : throw IOError . writeFailed ( errno: _ss_errno ( ) )
244+ case 0 : throw IOError . writeFailed ( errno: 0 ) /* unreachable */
245+ case let n: ptr += Int ( n)
246+ }
236247 }
237248}
238249
239- private extension FileHandle {
240- func _write( contentsOf data: Data ) throws {
241- if #available( macOS 10 . 15 . 4 , iOS 13 . 4 , watchOS 6 . 2 , tvOS 13 . 4 , * ) {
242- return try self . write ( contentsOf: data)
243- } else {
244- return self . write ( data)
250+ /// Fill the buffer from the file descriptor. Throws an error on failure.
251+ /// If the file descriptor reached the end-of-file before filling up the entire
252+ /// buffer, throws IOError.readReachedEndOfInput
253+ private func _read( _ fd: CInt , into buffer: UnsafeMutableRawBufferPointer ) throws {
254+ guard var ptr = buffer. baseAddress else { return }
255+ let endPtr = ptr. advanced ( by: buffer. count)
256+ while ptr != endPtr {
257+ switch read ( fd, ptr, numericCast ( endPtr - ptr) ) {
258+ case - 1 : throw IOError . readFailed ( errno: _ss_errno ( ) )
259+ case 0 : throw IOError . readReachedEndOfInput
260+ case let n: ptr += Int ( n)
245261 }
246262 }
263+ }
247264
248- func _read( upToCount count: Int ) throws -> Data ? {
249- if #available( macOS 10 . 15 . 4 , iOS 13 . 4 , watchOS 6 . 2 , tvOS 13 . 4 , * ) {
250- return try self . read ( upToCount: count)
251- } else {
252- return self . readData ( ofLength: 8 )
265+ private enum IOError : Error , CustomStringConvertible {
266+ case readReachedEndOfInput
267+ case readFailed( errno: CInt )
268+ case writeFailed( errno: CInt )
269+
270+ var description : String {
271+ switch self {
272+ case . readReachedEndOfInput: " read(2) reached end-of-file "
273+ case . readFailed( let errno) : " read(2) failed: \( describe ( errno: errno) ) "
274+ case . writeFailed( let errno) : " write(2) failed: \( describe ( errno: errno) ) "
253275 }
254276 }
255277}
256278
257- struct CompilerPluginError : Error , CustomStringConvertible {
258- var description : String
259- init ( message: String ) {
260- self . description = message
261- }
279+ // Private function to construct an error message from an `errno` code.
280+ private func describe( errno: CInt ) -> String {
281+ if let cStr = strerror ( errno) { return String ( cString: cStr) }
282+ return String ( describing: errno)
262283}
0 commit comments