@@ -66,33 +66,6 @@ public struct ExitTest: Sendable {
6666#endif
6767 }
6868
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-
9669 /// Call the exit test in the current process.
9770 ///
9871 /// This function invokes the closure originally passed to
@@ -103,26 +76,8 @@ public struct ExitTest: Sendable {
10376 public func callAsFunction( ) async -> Never {
10477 Self . _disableCrashReporting ( )
10578
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-
12479 do {
125- try await Configuration . withCurrent ( configuration , perform : body)
80+ try await body ( )
12681 } catch {
12782 _errorInMain ( error)
12883 }
@@ -276,6 +231,33 @@ extension ExitTest {
276231 /// recording any issues that occur.
277232 public typealias Handler = @Sendable ( _ exitTest: borrowing ExitTest ) async throws -> ExitCondition
278233
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+
279261 /// Find the exit test function specified in the environment of the current
280262 /// process, if any.
281263 ///
@@ -286,14 +268,48 @@ extension ExitTest {
286268 /// `__swiftPMEntryPoint()` function. The effect of using it under other
287269 /// configurations is undefined.
288270 static func findInEnvironmentForEntryPoint( ) -> Self ? {
271+ var result : Self ?
272+
289273 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
291275 let sourceLocationBuffer = UnsafeRawBufferPointer ( sourceLocationBuffer)
292276 let sourceLocation = try JSON . decode ( SourceLocation . self, from: sourceLocationBuffer)
293277 return find ( at: sourceLocation)
294278 }
295279 }
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
297313 }
298314
299315 /// The exit test handler used when integrating with Swift Package Manager via
0 commit comments