@@ -66,33 +66,6 @@ public struct ExitTest: Sendable {
66
66
#endif
67
67
}
68
68
69
- /// The back channel file handle set up by the parent process.
70
- ///
71
- /// The value of this property is a file handle open for writing to which
72
- /// events should be written, or `nil` if the file handle could not be
73
- /// resolved.
74
- private static let _backChannel : FileHandle ? = {
75
- guard let backChannelEnvironmentVariable = Environment . variable ( named: " SWT_EXPERIMENTAL_BACKCHANNEL " ) else {
76
- return nil
77
- }
78
-
79
- var fd : CInt ?
80
- #if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD)
81
- fd = CInt ( backChannelEnvironmentVariable)
82
- #elseif os(Windows)
83
- if let handle = UInt ( backChannelEnvironmentVariable) . flatMap ( HANDLE . init ( bitPattern: ) ) {
84
- fd = _open_osfhandle ( Int ( bitPattern: handle) , _O_WRONLY | _O_BINARY)
85
- }
86
- #else
87
- #warning("Platform-specific implementation missing: back-channel pipe unavailable")
88
- #endif
89
- guard let fd, fd >= 0 else {
90
- return nil
91
- }
92
-
93
- return try ? FileHandle ( unsafePOSIXFileDescriptor: fd, mode: " wb " )
94
- } ( )
95
-
96
69
/// Call the exit test in the current process.
97
70
///
98
71
/// This function invokes the closure originally passed to
@@ -103,26 +76,8 @@ public struct ExitTest: Sendable {
103
76
public func callAsFunction( ) async -> Never {
104
77
Self . _disableCrashReporting ( )
105
78
106
- // Set up the configuration for this process.
107
- var configuration = Configuration ( )
108
-
109
- // Encode events as JSON and write them to the back channel file handle.
110
- var eventHandler = ABIv0 . Record. eventHandler ( encodeAsJSONLines: true ) { json in
111
- try ? Self . _backChannel? . write ( json)
112
- }
113
-
114
- // Only forward issue-recorded events. (If we start handling other kinds
115
- // of event in the future, we can forward them too.)
116
- eventHandler = { [ eventHandler] event, eventContext in
117
- if case . issueRecorded = event. kind {
118
- eventHandler ( event, eventContext)
119
- }
120
- }
121
-
122
- configuration. eventHandler = eventHandler
123
-
124
79
do {
125
- try await Configuration . withCurrent ( configuration , perform : body)
80
+ try await body ( )
126
81
} catch {
127
82
_errorInMain ( error)
128
83
}
@@ -276,6 +231,33 @@ extension ExitTest {
276
231
/// recording any issues that occur.
277
232
public typealias Handler = @Sendable ( _ exitTest: borrowing ExitTest ) async throws -> ExitCondition
278
233
234
+ /// The back channel file handle set up by the parent process.
235
+ ///
236
+ /// The value of this property is a file handle open for writing to which
237
+ /// events should be written, or `nil` if the file handle could not be
238
+ /// resolved.
239
+ private static let _backChannelForEntryPoint : FileHandle ? = {
240
+ guard let backChannelEnvironmentVariable = Environment . variable ( named: " SWT_EXPERIMENTAL_BACKCHANNEL " ) else {
241
+ return nil
242
+ }
243
+
244
+ var fd : CInt ?
245
+ #if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD)
246
+ fd = CInt ( backChannelEnvironmentVariable)
247
+ #elseif os(Windows)
248
+ if let handle = UInt ( backChannelEnvironmentVariable) . flatMap ( HANDLE . init ( bitPattern: ) ) {
249
+ fd = _open_osfhandle ( Int ( bitPattern: handle) , _O_WRONLY | _O_BINARY)
250
+ }
251
+ #else
252
+ #warning("Platform-specific implementation missing: back-channel pipe unavailable")
253
+ #endif
254
+ guard let fd, fd >= 0 else {
255
+ return nil
256
+ }
257
+
258
+ return try ? FileHandle ( unsafePOSIXFileDescriptor: fd, mode: " wb " )
259
+ } ( )
260
+
279
261
/// Find the exit test function specified in the environment of the current
280
262
/// process, if any.
281
263
///
@@ -286,14 +268,48 @@ extension ExitTest {
286
268
/// `__swiftPMEntryPoint()` function. The effect of using it under other
287
269
/// configurations is undefined.
288
270
static func findInEnvironmentForEntryPoint( ) -> Self ? {
271
+ var result : Self ?
272
+
289
273
if var sourceLocationString = Environment . variable ( named: " SWT_EXPERIMENTAL_EXIT_TEST_SOURCE_LOCATION " ) {
290
- return try ? sourceLocationString. withUTF8 { sourceLocationBuffer in
274
+ result = try ? sourceLocationString. withUTF8 { sourceLocationBuffer in
291
275
let sourceLocationBuffer = UnsafeRawBufferPointer ( sourceLocationBuffer)
292
276
let sourceLocation = try JSON . decode ( SourceLocation . self, from: sourceLocationBuffer)
293
277
return find ( at: sourceLocation)
294
278
}
295
279
}
296
- return nil
280
+
281
+ // If an exit test was found, inject back channel handling into its body.
282
+ // External tools authors should set up their own back channel mechanisms
283
+ // and ensure they're installed before calling ExitTest.callAsFunction().
284
+ result = result. map { result in
285
+ // We can't say guard let here because it counts as a consume.
286
+ guard _backChannelForEntryPoint != nil else {
287
+ return result
288
+ }
289
+
290
+ // Set up the configuration for this process.
291
+ var configuration = Configuration ( )
292
+
293
+ // Encode events as JSON and write them to the back channel file handle.
294
+ // Only forward issue-recorded events. (If we start handling other kinds
295
+ // of event in the future, we can forward them too.)
296
+ let eventHandler = ABIv0 . Record. eventHandler ( encodeAsJSONLines: true ) { json in
297
+ try ? _backChannelForEntryPoint? . write ( json)
298
+ }
299
+ configuration. eventHandler = { event, eventContext in
300
+ if case . issueRecorded = event. kind {
301
+ eventHandler ( event, eventContext)
302
+ }
303
+ }
304
+
305
+ var result = result
306
+ result. body = { [ configuration, body = result. body] in
307
+ try await Configuration . withCurrent ( configuration, perform: body)
308
+ }
309
+ return result
310
+ }
311
+
312
+ return result
297
313
}
298
314
299
315
/// The exit test handler used when integrating with Swift Package Manager via
0 commit comments