Skip to content

Commit 3a5ff0e

Browse files
committed
Move PluginMessageConnection to PluginMessageHandling
So that other modules (e.g. swift-plugin-server can use it) Renamed to 'StandardIOMessageConnection'
1 parent 6dc8d43 commit 3a5ff0e

File tree

5 files changed

+185
-167
lines changed

5 files changed

+185
-167
lines changed

Package.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ let package = Package(
7373

7474
.target(
7575
name: "SwiftCompilerPlugin",
76-
dependencies: ["SwiftCompilerPluginMessageHandling", "SwiftSyntaxMacros", "_SwiftSyntaxCShims"],
76+
dependencies: ["SwiftCompilerPluginMessageHandling", "SwiftSyntaxMacros"],
7777
exclude: ["CMakeLists.txt"]
7878
),
7979

@@ -87,6 +87,7 @@ let package = Package(
8787
.target(
8888
name: "SwiftCompilerPluginMessageHandling",
8989
dependencies: [
90+
"_SwiftSyntaxCShims",
9091
"SwiftDiagnostics",
9192
"SwiftOperators",
9293
"SwiftParser",

Sources/SwiftCompilerPlugin/CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,4 @@ add_swift_syntax_library(SwiftCompilerPlugin
1414
target_link_swift_syntax_libraries(SwiftCompilerPlugin PUBLIC
1515
SwiftSyntaxMacros
1616
SwiftCompilerPluginMessageHandling
17-
_SwiftSyntaxCShims
1817
)

Sources/SwiftCompilerPlugin/CompilerPlugin.swift

Lines changed: 4 additions & 165 deletions
Original file line numberDiff line numberDiff line change
@@ -9,31 +9,13 @@
99
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
1010
//
1111
//===----------------------------------------------------------------------===//
12-
// NOTE: This basic plugin mechanism is mostly copied from
13-
// https://github.com/apple/swift-package-manager/blob/main/Sources/PackagePlugin/Plugin.swift
1412

1513
#if swift(>=6.0)
16-
private import _SwiftSyntaxCShims
1714
public import SwiftSyntaxMacros
1815
@_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
2616
#else
27-
import _SwiftSyntaxCShims
2817
import SwiftSyntaxMacros
2918
@_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
3719
#endif
3820

3921
//
@@ -122,163 +104,20 @@ struct MacroProviderAdapter<Plugin: CompilerPlugin>: PluginProvider {
122104
}
123105
}
124106

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-
134107
extension CompilerPlugin {
135108

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.
138111
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()
180113
let provider = MacroProviderAdapter(plugin: Self())
181114
let impl = CompilerPluginMessageListener(connection: connection, provider: provider)
182115
do {
183116
try impl.main()
184117
} catch {
185118
// Emit a diagnostic and indicate failure to the plugin host,
186119
// 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)")
247121
}
248122
}
249123
}
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-
}

Sources/SwiftCompilerPluginMessageHandling/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@ add_swift_syntax_library(SwiftCompilerPluginMessageHandling
1717
JSON/JSON.swift
1818
JSON/JSONDecoding.swift
1919
JSON/JSONEncoding.swift
20+
StandardIOMessageConnection.swift
2021
)
2122

2223
target_link_swift_syntax_libraries(SwiftCompilerPluginMessageHandling PUBLIC
24+
_SwiftSyntaxCShims
2325
SwiftSyntax
2426
SwiftBasicFormat
2527
SwiftDiagnostics

0 commit comments

Comments
 (0)