diff --git a/Sources/Testing/Support/Additions/CommandLineAdditions.swift b/Sources/Testing/Support/Additions/CommandLineAdditions.swift index fa51c9550..5d5b86d94 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 !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