|
| 1 | +# API to get the path to the current executable |
| 2 | + |
| 3 | +* Proposal: [SE-0513](0513-commandline-executablepath.md) |
| 4 | +* Authors: [Jonathan Grynspan](https://github.com/grynspan) |
| 5 | +* Review Manager: [Tony Allevato](https://github.com/allevato) |
| 6 | +* Status: **Active review (February 18–March 4, 2026)** |
| 7 | +* Implementation: [swiftlang/swift#85496](https://github.com/swiftlang/swift/pull/85496) |
| 8 | +* Review: ([pitch](https://forums.swift.org/t/pitch-api-to-get-the-path-to-the-current-executable/84137)) |
| 9 | + |
| 10 | +## Introduction |
| 11 | + |
| 12 | +This proposal adds to the Swift standard library an interface for reading the |
| 13 | +path to the currently-executing binary. This value is useful to developers who |
| 14 | +need to spawn additional processes or who need to present information about the |
| 15 | +current program to the user. |
| 16 | + |
| 17 | +## Motivation |
| 18 | + |
| 19 | +There is no portable way (e.g. a C standard library function or POSIX |
| 20 | +specification) to get the path to the current executable. Historically, |
| 21 | +developers have to write their own platform-specific implementation to get this |
| 22 | +value or have reached for `argv[0]` thinking it contains said path. We can do |
| 23 | +better and provide a common interface for this functionality across the |
| 24 | +platforms that Swift supports. |
| 25 | + |
| 26 | +> [!NOTE] |
| 27 | +> Regarding `argv[0]`: `argv[0]` is not required by POSIX nor any revision of |
| 28 | +> the C language standard to contain the path to the executable. From §5.1.2.3.2 |
| 29 | +> of the C23 standard (entitled "Program startup"): |
| 30 | +> |
| 31 | +> > If the value of `argc` is greater than zero, the string pointed to by |
| 32 | +> > `argv[0]` represents the program name; `argv[0][0]` shall be the null |
| 33 | +> > character if the program name is not available from the host environment. |
| 34 | +> |
| 35 | +> In practice, `argv[0]` is controlled by the parent process and may contain a |
| 36 | +> relative or partial path or even an unrelated string. |
| 37 | +
|
| 38 | +Swift, like most modern languages, provides access to information about the |
| 39 | +program's environment such as its command-line arguments (including `argv[0]`). |
| 40 | +Those arguments are read by the Swift runtime in a platform-specific manner. |
| 41 | +We should take a similar approach to read the executable path and provide it to |
| 42 | +developers if needed. |
| 43 | + |
| 44 | +There are a number of modules in the Swift toolchain and its ecosystem that |
| 45 | +would benefit from a way to get the executable path, including: |
| 46 | + |
| 47 | +- Foundation |
| 48 | +- Swift Argument Parser |
| 49 | +- Swift Testing |
| 50 | +- swift-subprocess |
| 51 | + |
| 52 | +## Proposed solution |
| 53 | + |
| 54 | +I propose adding a new read-only string property named `executablePath` to the |
| 55 | +existing [`CommandLine`](https://developer.apple.com/documentation/swift/commandline) |
| 56 | +type in the standard library. |
| 57 | + |
| 58 | +### Precedent in other languages |
| 59 | + |
| 60 | +While C and C++ (without Boost) do not provide an equivalent API, other modern |
| 61 | +languages do: |
| 62 | + |
| 63 | +| Language | Equivalent API | |
| 64 | +|-|-| |
| 65 | +| C++ (with Boost) | [`boost::dll::program_location()`](https://www.boost.org/doc/libs/latest/doc/html/doxygen/shared_library_reference/runtime__symbol__info_8hpp_1ad4f62eae484acfa53de57045fd18dde7.html) | |
| 66 | +| D | [`std.file.thisExePath()`](https://dlang.org/library/std/file/this_exe_path.html) | |
| 67 | +| Go | [`os.Executable()`](https://pkg.go.dev/os#Executable) | |
| 68 | +| Haskell | [`System.Environment.getExecutablePath`](https://hackage-content.haskell.org/package/base-4.22.0.0/docs/System-Environment.html) | |
| 69 | +| Rust | [`std::env::current_exe()`](https://doc.rust-lang.org/std/env/fn.current_exe.html) | |
| 70 | +| Zig | [`std.fs.selfExePath()`](https://ziglang.org/documentation/0.15.2/std/#std.fs.selfExePath) | |
| 71 | + |
| 72 | +## Detailed design |
| 73 | + |
| 74 | +The following new API is added to the standard library: |
| 75 | + |
| 76 | +```swift |
| 77 | +extension CommandLine { |
| 78 | + /// The path to the current executable. |
| 79 | + /// |
| 80 | + /// The value of this property may not be canonical. If you need the canonical |
| 81 | + /// path to the current executable, you can pass the value of this property to |
| 82 | + /// `realpath()` (`_wfullpath()` on Windows) or use `URL` to standardize the |
| 83 | + /// path. |
| 84 | + /// |
| 85 | + /// If the path to the current executable could not be determined, the value |
| 86 | + /// of this property is `nil`. |
| 87 | + /// |
| 88 | + /// - Important: On some systems, it is possible to move an executable file on |
| 89 | + /// disk while it is running. If the current executable file is moved, the |
| 90 | + /// value of this property is not updated to its new path. |
| 91 | +#if hasFeature(Embedded) || os(WASI) |
| 92 | + @available(*, unavailable) |
| 93 | +#endif |
| 94 | + public static var executablePath: String? { get } |
| 95 | +} |
| 96 | +``` |
| 97 | + |
| 98 | +The implementation does not attempt to resolve symlinks or other forms of |
| 99 | +indirection in the path provided by the underlying OS API call[^linuxRealpath]. |
| 100 | +This is a pragmatic decision: in the common case, a symlink is not present and |
| 101 | +the I/O necessary to try and resolve it is wasted effort. If there _is_ a |
| 102 | +symlink, its presence is not necessarily a problem for the calling code. Callers |
| 103 | +that need to resolve symlinks in this path can manually call `realpath()` |
| 104 | +(`_wfullpath()` on Windows) or equivalent API as needed. |
| 105 | + |
| 106 | +If the current executable is moved on disk after it starts, the underlying |
| 107 | +system may or may not update the path it reports. This is ultimately a |
| 108 | +platform-specific implementation detail and one that we cannot reliably work |
| 109 | +around, but neither can developers who implement their own version of this |
| 110 | +property. The documentation therefore warns developers of the possibility. |
| 111 | + |
| 112 | +This property is explicitly unavailable in Embedded Swift and WASI: if in the |
| 113 | +future we can reliably get a value for this property in Embedded Swift or on |
| 114 | +WASI, we ought to be able to lift these constraints. |
| 115 | + |
| 116 | +[^linuxRealpath]: On some platforms (namely Linux) the API itself consists of |
| 117 | + resolving a symlink, and we _do_ resolve that symlink out of necessity. |
| 118 | + |
| 119 | +## Source compatibility |
| 120 | + |
| 121 | +This change is additive. Developers (if any) who have already added an |
| 122 | +`executablePath` property to `CommandLine` may need to rename that property or |
| 123 | +replace it with this one. |
| 124 | + |
| 125 | +## ABI compatibility |
| 126 | + |
| 127 | +This proposal is purely an extension of the standard library which |
| 128 | +can be implemented without any ABI support. |
| 129 | + |
| 130 | +On Darwin, the property can be back-deployed because it is implemented atop |
| 131 | +existing API provided by the operating system. |
| 132 | + |
| 133 | +## Implications on adoption |
| 134 | + |
| 135 | +This feature can be freely adopted and un-adopted in source |
| 136 | +code with no deployment constraints and without affecting source or ABI |
| 137 | +compatibility. |
| 138 | + |
| 139 | +## Future directions |
| 140 | + |
| 141 | +N/A |
| 142 | + |
| 143 | +## Alternatives considered |
| 144 | + |
| 145 | +- Doing nothing. |
| 146 | + |
| 147 | +- **Using the implementation and interfaces in Foundation (e.g. |
| 148 | + [`Bundle.executableURL`](https://developer.apple.com/documentation/foundation/bundle/executableurl)).** |
| 149 | + Foundation is commonly imported by Swift packages and projects, but it is |
| 150 | + fairly "high up" in the stack. Some components _cannot_ link to it (such as |
| 151 | + the standard library) or have significant constraints when linking to it (such |
| 152 | + as Swift Testing). |
| 153 | + |
| 154 | +- **Providing API in the swift-system package.** swift-system's [`FilePath`](https://developer.apple.com/documentation/System/FilePath) |
| 155 | + type is appealing here, of course. But swift-system is non-portable by design, |
| 156 | + and the goal here is to provide a portable (or mostly-portable) API that can |
| 157 | + be used in cross-platform code. |
| 158 | + |
| 159 | +- **Exposing the property as a C string rather than as a Swift string.** We |
| 160 | + could provide an interface that produces an `UnsafePointer<CChar>` (or |
| 161 | + `UnsafePointer<CWideChar>` on Windows), a `Span<CChar>`, a |
| 162 | + `ContiguousArray<CChar>`, etc. We could still provide such an interface if |
| 163 | + needed, but paths are generally treated as strings in Swift code and in the |
| 164 | + common case a developer who is handed an `UnsafePointer<CChar>` is going to |
| 165 | + immediately pass it to `String.init(cString:)` anyway. |
| 166 | + |
| 167 | +- **Making the property's type non-optional.** The initial version of this |
| 168 | + proposal presented a non-optional property that aborted if the path was |
| 169 | + unavailable. This makes it difficult for developers to recover from a |
| 170 | + low-level failure, but a failure to get the executable path does not |
| 171 | + necessarily imply a fatal error in the program. |
| 172 | + |
| 173 | +- **Making the property's getter throwing.** The failure modes for this property |
| 174 | + are all edge cases. Throwing an error is a bit too "heavyweight" for the API's |
| 175 | + expected use cases. It is unlikely the program or the user could recover from |
| 176 | + a thrown error in a way that would allow the API to succeed the next time it |
| 177 | + is called, so callers would probably end up ignoring errors with `try?` or |
| 178 | + similar. |
| 179 | + |
| 180 | +- **Making the property available (always equalling `nil`) on WASI.** The |
| 181 | + property's value is optional on platforms where it is supported for reasons |
| 182 | + described earlier in this section. On WASI, there is no real concept of an |
| 183 | + "executable" within the WebAssembly virtual machine. If a developer is using |
| 184 | + this API and ports their code to WASI, and there is no compile-time indication |
| 185 | + that the property is non-functional, that developer may be misled into |
| 186 | + thinking the code works correctly. A better option is to mark the API |
| 187 | + unavailable so that a developer who is using it will be forced to stop and |
| 188 | + think about appropriate alternatives. |
| 189 | + |
| 190 | +- **Making the property available under Embedded Swift.** Under Embedded Swift, |
| 191 | + the Swift runtime has limited ability to call platform-specific API because |
| 192 | + there's no real guarantee there is even a "platform" _per se_. Embedded Swift |
| 193 | + can be used with full desktop-class operating systems, but it can also be used |
| 194 | + on true embedded systems where the CPU directly executes instructions loaded |
| 195 | + from RAM or ROM and there isn't even a file system in which to place an |
| 196 | + executable. As with WASI, it is better to mark the API unavailable so that a |
| 197 | + developer is forced to think about why they are trying to use it under |
| 198 | + Embedded Swift and whether it's appropriate to use in the first place. |
0 commit comments