Skip to content

Commit 7c37f5f

Browse files
committed
Move PluginMessageConnection to PluginMessageHandling
So that other modules (e.g. swift-plugin-server can use it) Renamed to 'StandardIOMessageConnection'
1 parent 27a1c07 commit 7c37f5f

File tree

5 files changed

+182
-167
lines changed

5 files changed

+182
-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
//
@@ -117,163 +99,20 @@ struct MacroProviderAdapter<Plugin: CompilerPlugin>: PluginProvider {
11799
}
118100
}
119101

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-
129102
extension CompilerPlugin {
130103

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

Sources/SwiftCompilerPluginMessageHandling/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ add_swift_syntax_library(SwiftCompilerPluginMessageHandling
2020
)
2121

2222
target_link_swift_syntax_libraries(SwiftCompilerPluginMessageHandling PUBLIC
23+
_SwiftSyntaxCShims
2324
SwiftSyntax
2425
SwiftBasicFormat
2526
SwiftDiagnostics

0 commit comments

Comments
 (0)