Skip to content

Commit 07593df

Browse files
authored
Merge pull request #3078 from swiftlang/jgrynspan/commandline-executablepath
[SE-0513] API to get the path to the current executable
2 parents 42d4bc0 + e810271 commit 07593df

1 file changed

Lines changed: 198 additions & 0 deletions

File tree

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
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

Comments
 (0)