Skip to content

Avoid truncating the path to the test executable on Windows. #724

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Sep 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Sources/Testing/ExitTests/SpawnProcess.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ func spawnExecutable(
) throws -> ProcessID {
// Darwin and Linux differ in their optionality for the posix_spawn types we
// use, so use this typealias to paper over the differences.
#if SWT_TARGET_OS_APPLE
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drive-by fix: FreeBSD defines these POSIX types as pointers to opaque structs, so they get imported similarly to Darwin (where they're just raw pointers) rather than Linux (where they're actually structures.)

#if SWT_TARGET_OS_APPLE || os(FreeBSD)
typealias P<T> = T?
#elseif os(Linux) || os(FreeBSD)
#elseif os(Linux)
typealias P<T> = T
#endif

Expand Down
42 changes: 30 additions & 12 deletions Sources/Testing/Support/Additions/CommandLineAdditions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,19 @@ extension CommandLine {
get throws {
#if os(macOS)
var result: String?
var bufferCount = UInt32(1024)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drive-by fix to use a named constant.

#if DEBUG
var bufferCount = UInt32(1) // force looping
#else
var bufferCount = UInt32(PATH_MAX)
#endif
while result == nil {
result = withUnsafeTemporaryAllocation(of: CChar.self, capacity: Int(bufferCount)) { buffer in
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's consistently set result in the buffer's scope rather than returning it. This saves us a return nil line and will be easier to translate if/when we get non-escaping buffers.

withUnsafeTemporaryAllocation(of: CChar.self, capacity: Int(bufferCount)) { buffer in
// _NSGetExecutablePath returns 0 on success and -1 if bufferCount is
// too small. If that occurs, we'll return nil here and loop with the
// new value of bufferCount.
if 0 == _NSGetExecutablePath(buffer.baseAddress, &bufferCount) {
return String(cString: buffer.baseAddress!)
result = String(cString: buffer.baseAddress!)
}
return nil
}
}
return result!
Expand All @@ -40,7 +43,7 @@ extension CommandLine {
}
#elseif os(FreeBSD)
var mib = [CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1]
try mib.withUnsafeMutableBufferPointer { mib in
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drive-by fix.

return try mib.withUnsafeMutableBufferPointer { mib in
var bufferCount = 0
guard 0 == sysctl(mib.baseAddress!, .init(mib.count), nil, &bufferCount, nil, 0) else {
throw CError(rawValue: swt_errno())
Expand All @@ -53,15 +56,30 @@ extension CommandLine {
}
}
#elseif os(Windows)
return try withUnsafeTemporaryAllocation(of: wchar_t.self, capacity: Int(MAX_PATH) * 2) { buffer in
guard 0 != GetModuleFileNameW(nil, buffer.baseAddress!, DWORD(buffer.count)) else {
throw Win32Error(rawValue: GetLastError())
}
guard let path = String.decodeCString(buffer.baseAddress!, as: UTF16.self)?.result else {
throw Win32Error(rawValue: DWORD(ERROR_ILLEGAL_CHARACTER))
var result: String?
#if DEBUG
var bufferCount = Int(1) // force looping
#else
var bufferCount = Int(MAX_PATH)
#endif
while result == nil {
try withUnsafeTemporaryAllocation(of: wchar_t.self, capacity: bufferCount) { buffer in
SetLastError(DWORD(ERROR_SUCCESS))
_ = GetModuleFileNameW(nil, buffer.baseAddress!, DWORD(buffer.count))
switch GetLastError() {
case DWORD(ERROR_SUCCESS):
result = String.decodeCString(buffer.baseAddress!, as: UTF16.self)?.result
if result == nil {
throw Win32Error(rawValue: DWORD(ERROR_ILLEGAL_CHARACTER))
}
case DWORD(ERROR_INSUFFICIENT_BUFFER):
bufferCount += Int(MAX_PATH)
case let errorCode:
throw Win32Error(rawValue: errorCode)
}
}
return path
}
return result!
#elseif os(WASI)
// WASI does not really have the concept of a file system path to the main
// executable, so simply return the first argument--presumably the program
Expand Down