Skip to content

Commit 4c1286e

Browse files
authored
Special-case FileHandle(forWritingAtPath: "CONOUT$") on Windows. (#654)
This PR special-cases calls to `FileHandle.init(forWritingAtPath:)` when passed `"CONOUT$"` (the reserved "console output" file) on Windows such that it does not open a new file but just returns a copy of `.stdout` instead. This change is necessary because, on Windows, opening and locking `"CONOUT$"` does not lock `stdout` (they're considered entirely different files) and output written to `"CONOUT$"` is wrapped at the console's column limit even though it's being written as binary data. The VSCode Swift plugin authors would like to be able to specify writing the JSON event stream to `"CONOUT$"`, but the observed behaviour is stopping them from doing so. ### Checklist: - [x] Code and documentation should follow the style of the [Style Guide](https://github.com/apple/swift-testing/blob/main/Documentation/StyleGuide.md). - [x] If public symbols are renamed or modified, DocC references should be updated.
1 parent d00d469 commit 4c1286e

File tree

1 file changed

+15
-0
lines changed

1 file changed

+15
-0
lines changed

Sources/Testing/Support/FileHandle.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,21 @@ struct FileHandle: ~Copyable, Sendable {
5252
/// - Throws: Any error preventing the stream from being opened.
5353
init(atPath path: String, mode: String) throws {
5454
#if os(Windows)
55+
// Special-case CONOUT$ to map to stdout. This way, if somebody specifies
56+
// CONOUT$ as the target path for XML or JSON output from `swift test`,
57+
// output will be correctly interleaved with writes to `stdout`. If we don't
58+
// do this, the file will open successfully but will be opened in text mode
59+
// (even if we ask for binary mode), will wrap at the virtual console's
60+
// column limit, and won't share a file lock with the C `stdout` handle.
61+
//
62+
// To our knowledge, this sort of special-casing is not required on
63+
// POSIX-like platforms (i.e. when opening "/dev/stdout"), but it can be
64+
// adapted for use there if some POSIX-like platform does need it.
65+
if path == "CONOUT$" && mode.contains("w") {
66+
self = .stdout
67+
return
68+
}
69+
5570
// Windows deprecates fopen() as insecure, so call _wfopen_s() instead.
5671
let fileHandle = try path.withCString(encodedAs: UTF16.self) { path in
5772
try mode.withCString(encodedAs: UTF16.self) { mode in

0 commit comments

Comments
 (0)