From 43894c3c6f45d4866718de202400e5547bfa22ae Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Fri, 17 Jul 2026 13:41:54 -0400 Subject: [PATCH 1/2] Adopt executable path API (sorta) from the stdlib. https://github.com/swiftlang/swift/pull/90476 added an exported function to the stdlib on non-Apple platforms to get the current executable's path. We should adopt this implementation instead of continuing to use our own. On Apple platforms, due to back-deployment requirements, that function is not implemented and we instead continue to call `_NSGetExecutablePath()`. I have cherry-picked the change to 6.4.x [here](https://github.com/swiftlang/swift/pull/90659), so the change should work there too once the toolchain builds a new nightly. --- .../Additions/CommandLineAdditions.swift | 107 +++++------------- Sources/_TestingInternals/CMakeLists.txt | 1 - Sources/_TestingInternals/ExecutablePath.cpp | 39 ------- .../include/ExecutablePath.h | 31 ----- 4 files changed, 26 insertions(+), 152 deletions(-) delete mode 100644 Sources/_TestingInternals/ExecutablePath.cpp delete mode 100644 Sources/_TestingInternals/include/ExecutablePath.h diff --git a/Sources/Testing/Support/Additions/CommandLineAdditions.swift b/Sources/Testing/Support/Additions/CommandLineAdditions.swift index fa51c9550..6a42547f8 100644 --- a/Sources/Testing/Support/Additions/CommandLineAdditions.swift +++ b/Sources/Testing/Support/Additions/CommandLineAdditions.swift @@ -26,12 +26,27 @@ enum CommandLine { } #endif -#if !SWT_NO_EXIT_TESTS extension CommandLine { +#if compiler(>=6.5) && !hasFeature(Embedded) && !os(WASI) && !SWT_TARGET_OS_APPLE +#if os(Windows) + private typealias FPEncoding = UTF16 +#else + private typealias FPEncoding = UTF8 +#endif + + private static var executablePathCString: ContiguousArray? { + @_silgen_name("_swift_stdlib_executablePathCString") get + } +#endif + /// The path to the current process' executable. static var executablePath: String { get throws { -#if SWT_TARGET_OS_APPLE +#if hasFeature(Embedded) || os(WASI) + // Embedded Swift and WASI do not currently support getting the executable + // path via the standard library. + throw SystemError(description: "The current executable path is not available on this platform.") +#elseif SWT_TARGET_OS_APPLE var result: String? #if DEBUG var bufferCount = UInt32(1) // force looping @@ -49,91 +64,21 @@ extension CommandLine { } } return result! -#elseif os(Linux) || os(Android) - var result: String? -#if DEBUG - var bufferCount = Int(1) // force looping #else - var bufferCount = Int(PATH_MAX) -#endif - while result == nil { - try withUnsafeTemporaryAllocation(of: CChar.self, capacity: bufferCount) { buffer in - let readCount = readlink("/proc/self/exe", buffer.baseAddress!, buffer.count) - guard readCount >= 0 else { - throw CError(rawValue: swt_errno()) - } - if readCount < buffer.count { - buffer[readCount] = 0 // NUL-terminate the string. - result = String(cString: buffer.baseAddress!) - } else { - bufferCount += Int(PATH_MAX) // add more space and try again - } - } - } - return result! -#elseif os(FreeBSD) - var mib = [CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1] - 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()) - } - return try withUnsafeTemporaryAllocation(of: CChar.self, capacity: bufferCount) { buffer in - guard 0 == sysctl(mib.baseAddress!, .init(mib.count), buffer.baseAddress!, &bufferCount, nil, 0) else { - throw CError(rawValue: swt_errno()) - } - return String(cString: buffer.baseAddress!) - } - } -#elseif os(OpenBSD) - // OpenBSD does not have API to get a path to the running executable. Use - // arguments[0]. We do a basic sniff test for a path-like string, and - // prepend the early CWD if it looks like a relative path, but otherwise - // return argv[0] verbatim. - guard var argv0 = arguments.first, argv0.contains("/") else { - throw CError(rawValue: ENOEXEC) - } - if argv0.first != "/", - let earlyCWD = swt_getEarlyCWD().flatMap(String.init(validatingCString:)), - !earlyCWD.isEmpty { - argv0 = "\(earlyCWD)/\(argv0)" - } - return argv0 -#elseif os(Windows) - var result: String? -#if DEBUG - var bufferCount = Int(1) // force looping + guard let executablePathCString else { +#if os(Windows) + throw Win32Error(rawValue: GetLastError()) #else - var bufferCount = Int(MAX_PATH) + throw CError(rawValue: swt_errno()) #endif - while result == nil { - try withUnsafeTemporaryAllocation(of: CWideChar.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 try executablePathCString.withUnsafeBufferPointer { executablePathCString in + guard let result = String.decodeCString(executablePathCString.baseAddress!, as: FPEncoding.self)?.result else { + throw SystemError(description: "Could not decode the current executable's path as \(FPEncoding.self).") } + return result } - 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 - // name, but as you know this is not guaranteed by the C standard! - return arguments[0] -#else -#warning("Platform-specific implementation missing: executable path unavailable") - throw SystemError(description: "The executable path of the current process could not be determined.") #endif } } } -#endif diff --git a/Sources/_TestingInternals/CMakeLists.txt b/Sources/_TestingInternals/CMakeLists.txt index 8732f421b..9240b545b 100644 --- a/Sources/_TestingInternals/CMakeLists.txt +++ b/Sources/_TestingInternals/CMakeLists.txt @@ -11,7 +11,6 @@ set(CMAKE_CXX_SCAN_FOR_MODULES 0) include(GitCommit) include(TargetTriple) add_library(_TestingInternals STATIC - ExecutablePath.cpp Versions.cpp WillThrow.cpp) target_include_directories(_TestingInternals PUBLIC diff --git a/Sources/_TestingInternals/ExecutablePath.cpp b/Sources/_TestingInternals/ExecutablePath.cpp deleted file mode 100644 index ba0c2b0dd..000000000 --- a/Sources/_TestingInternals/ExecutablePath.cpp +++ /dev/null @@ -1,39 +0,0 @@ -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2025 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for Swift project authors -// - -#include "ExecutablePath.h" - -#include - -#if defined(__OpenBSD__) -/// Storage for ``swt_getEarlyCWD()``. -static constinit std::atomic earlyCWD { nullptr }; - -/// At process start (before `main()` is called), capture the current working -/// directory. -/// -/// This function is necessary on OpenBSD so that we can (as correctly as -/// possible) resolve the executable path when the first argument is a relative -/// path (which can occur when manually invoking the test executable.) -__attribute__((__constructor__(101), __used__)) -static void captureEarlyCWD(void) { - if (auto cwd = getcwd(nullptr, 0)) { - earlyCWD.store(cwd); - } -} -#endif - -const char *swt_getEarlyCWD(void) { -#if defined(__OpenBSD__) - return earlyCWD.load(); -#else - return nullptr; -#endif -} diff --git a/Sources/_TestingInternals/include/ExecutablePath.h b/Sources/_TestingInternals/include/ExecutablePath.h deleted file mode 100644 index dfa9b1e7e..000000000 --- a/Sources/_TestingInternals/include/ExecutablePath.h +++ /dev/null @@ -1,31 +0,0 @@ -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2025 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for Swift project authors -// - -#if !defined(SWT_EXECUTABLE_PATH_H) -#define SWT_EXECUTABLE_PATH_H - -#include "Defines.h" -#include "Includes.h" - -SWT_ASSUME_NONNULL_BEGIN - -/// Get the current working directory as it was set shortly after the process -/// started and before `main()` has been called. -/// -/// This function is necessary on OpenBSD so that we can (as correctly as -/// possible) resolve the executable path when the first argument is a relative -/// path (which can occur when manually invoking the test executable.) -/// -/// On all other platforms, this function always returns `nullptr`. -SWT_EXTERN const char *_Nullable swt_getEarlyCWD(void); - -SWT_ASSUME_NONNULL_END - -#endif From d9e104ee9060cc148daac8f9aa3e3a437a639a05 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Thu, 23 Jul 2026 14:48:24 -0400 Subject: [PATCH 2/2] Fix typo --- Sources/Testing/Support/Additions/CommandLineAdditions.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Testing/Support/Additions/CommandLineAdditions.swift b/Sources/Testing/Support/Additions/CommandLineAdditions.swift index 6a42547f8..5d5b86d94 100644 --- a/Sources/Testing/Support/Additions/CommandLineAdditions.swift +++ b/Sources/Testing/Support/Additions/CommandLineAdditions.swift @@ -27,7 +27,7 @@ enum CommandLine { #endif extension CommandLine { -#if compiler(>=6.5) && !hasFeature(Embedded) && !os(WASI) && !SWT_TARGET_OS_APPLE +#if !hasFeature(Embedded) && !os(WASI) && !SWT_TARGET_OS_APPLE #if os(Windows) private typealias FPEncoding = UTF16 #else