SE-0513: API to get the path to the current executable

Hello, Swift community!

The review of SE-0513: API to get the path to the current executable begins now and runs through March 4th, 2026.

Reviews are an important part of the Swift evolution process. All review feedback should be either on this forum thread or, if you would like to keep your feedback private, directly to me as the review manager by DM. When contacting the review manager directly, please put "SE-0513" in the subject line.

What goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

  • What is your evaluation of the proposal?
  • Is the problem being addressed significant enough to warrant a change to Swift?
  • Does this proposal fit well with the feel and direction of Swift?
  • If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

More information about the Swift evolution process is available in the Swift evolution repository.

With thanks in advance for your consideration,

Tony Allevato
Review Manager

14 Likes

A big +1 here, this saves lots of headaches!

4 Likes

+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1

2 Likes

Just to have it specified explicitly: if the underlying OS API returns a path that is not valid Unicode, does this function return nil or attempt to repair the text so there’s still something to attempt to use?

Will the path always be absolute if present, or can I get a relative path or basename only back from this API?

On Windows, is anything promised about the format of the returned path? I’m guessing “nothing other than that it can be canonicalized”, but just to make sure.

7 Likes

+1 to the proposal as written! Yes please!


I have had this issue myself when I wanted to run a second copy of my own program as a subprocess.

I think the placement as a static property on CommandLine seems right. I’m glad it’s proposed as a String rather than a C string.

The only question I have is about the justification re. not choosing FilePath - there’s ongoing discussion about sinking System into the toolchain and while everyone seems to disagree about that, the one point of near universal agreement in that thread is that we should get FilePath into the toolchain. It’s true that System isn’t meant to be cross platform, but it is meant to “avoid unnecessary differences” (from the README) and as far as I can tell it is unconditionally available on every platform System builds for. And I’m hard pressed to think of a platform where “executable path” makes sense as a concept but “file path” does not. Especially since FilePath is already useful for both UNIXy paths and Windowsy paths. Does it really make sense to avoid FilePath for cross platform abstractions which are dealing in the concept of a path?

4 Likes

The current implementation does not attempt to repair the string if it isn't valid Unicode.

As I mentioned in the alternatives considered section, I'm anticipating that most consumers of this API will immediately shove it in a String if it isn't already a String. We do that with the program's command-line arguments (CommandLine.arguments) too, and there's a good chance that the argument list of a program chosen at random will include one or more file system paths.

It doesn't feel quite right to me to offer an additional executablePathCString: ContiguousArray<CChar>-shaped property, but it's easy enough to do so if the community needs it. See also my comments about swift-system below.

My intent is that you'll get back an absolute path. There may be platform-specific corner cases I'm not aware of where you'll get back something else, so please file bugs if you run into such problems.

Microsoft's documentation states:

The string returned will use the same format that was specified when the module was loaded. Therefore, the path can be a long or short file name, and can use the prefix "?". For more information, see Naming a File.

I'm not sure if that answers your question?

This is not part of the proposal, but it's my opinion that the standard library should include a type representing a file path distinct from a Unicode string. If there were such a type, it would definitely change the calculus here. :slight_smile:

swift-system is fairly high in the stack and not part of the (public interface of the) Swift toolchain. That means that components in the toolchain can't rely on it being present and can't link to it. There are multiple components in the toolchain that do need the executable path. The ones I know of off the top of my head are:

  • Foundation
  • Swift Testing
  • Swift Argument Parser
  • Swift Package Manager

These components can't link to swift-system, so each one needs to implement its own logic to find the executable path. A single implementation in the standard library would allow each of these components to share a common implementation, bug fixes and all.

I'm aware there's a pitch up to move swift-system into the toolchain. If executablePath is in the standard library, swift-system would be able to expose it as a FilePath too. Changes to swift-system are not part of this proposal, but let's assume that as a toolchain component, swift-system could get the value without a trip through Unicode Land.

1 Like

+1!

Thanks, I appreciate the response, but this is not why I was asking the questions. The API should commit to and document its choices in all of these, because they affect how people might use it.

4 Likes

I understand. We can update the implementation PR to make these details clearer without affecting the proposal itself. The exact wording of documentation comments can and does vary from what's written in a proposal.

My biggest use for such an API is to find resource files bundled with the executable.

On some platforms, the returned executable path can be a symbolic link. In order to deal with that, I would need to use the platform-local equivalent of Darwin's realpath(3) to canonicalize the returned path. realpath is not cross-platform (it's a BSD-ism), so if I attempt to use executablePath for its cross-platform-ness I immediately run into the need for more platform-specific code.

I know that the current behavior of executablePath matches eg. Rust, but Rust's stdlib includes Path::canonicalize which can be used to achieve the above in a cross-platform manner. Swift's does not. So I think the calculus is different.

I think on the whole my preference is that this API should wait on the decision as to where FilePath ends up, and land there. And also that a cross-platform FilePath.canonicalize be added in the same place at the same time.

3 Likes

What sorts of invalid are we talking about here?

(Emphasis added.)

If valid UTF-8 cannot be guaranteed but the implementation must bypass validation for whatever reason (e.g., performance), then it seems this needs to be an unsafe API or it needs to use some foreign storage representation so that validation is lazily triggered where it matters.

[Edit: the implementation does seem to use validating APIs on String—which, unless I'm misunderstanding, means that this API does return nil if there's invalid UTF-8.]

2 Likes

Right, we're saying the same thing. If the string is invalid Unicode, it isn't a valid String and will come back as nil. This is consistent with CommandLine.arguments. Sorry if that wasn't clear!

2 Likes

Neither Windows nor Linux guarantees that all paths are valid UTF-8; on Linux they can be in an arbitrary 8-bit encoding, on Windows my understanding is that although they notionally use a unicode encoding, it's not necessarily validated strictly.

1 Like

For what it's worth, realpath(3) is standardized in POSIX. The drop-in equivalent on Windows is _wfullpath(). Between the two, you've covered all platforms for which the proposed API has an implementation.

I could see a func executablePath(resolvingSymbolicLinks: Bool) variant of this interface (name it however you like), but in the general case we don't want to resolve them automatically for two reasons:

  1. It's not cheap (better hope the symlink doesn't cross into an NFS mount!), and
  2. The behavior of a program may change depending on the value of this property (e.g. swiftc is a symlink to swift-frontend but behaves differently when invoked as swiftc).

Even if swift-system gets added to the toolchain, I wouldn't be able to use it in Swift Testing because Swift Testing's deployment target is lower than swift-system's on Darwin. There's some sort of irony in a proposal author being unable to adopt their proposed API. :thinking:

As the executable path is needed in libraries that can't link swift-system, the end result here may be that we provide a low-level toolchain-only interface that produces the C string and let those libraries and swift-system share the implementation. That would be a better outcome than Swift Testing and swift-system duplicating the same logic.

3 Likes

While I am very sympathetic to swift-testing's problem here I think we shouldn't over optimize for it. The overarching feedback in the "swift-system in the toolchain" thread was that there was almost unilateral agreement that a FilePath type in one of Swift's standard library modules is desired. It seems very unfortunate if we add a new API to get the path of an executable which then isn't using the hypothetical stdlib FilePath. To not block this proposal but leaving a future direction open to use a FilePath type what if we rename the proposed property to executablePathString?

5 Likes

I amended my answer but I think you missed the change. :slightly_smiling_face: If the community prefers it landing in swift-system, we'll need something for toolchain consumption in the stdlib/runtime that both swift-system and Swift Testing can consume, but that's trivial to adapt from the current PR and we can make it work.

I don't mean to suggest that we should design our API around Swift Testing's unique needs; it's just ironic/amusing if I, code owner of Swift Testing and author of this proposal, can't use my own API. :melting_face:

I'm open to name changes here!

Something to keep in mind: CommandLine.arguments has a similar problem where the arguments may not be valid Unicode and may represent paths (or arbitrary other null-terminated data, really). We ought to consider this API in the context of the existing API. That may mean subjecting it to the same constraints as CommandLine.arguments or it may mean we want to replace that property with something encoding-agnostic too. (Although that's all probably beyond the scope of this proposal anyway).

Oh, I'm completely convinced that executablePath should not itself resolve symlinks! My concern is only that in general you need both the executablePath and the ability to canonicalize that path, and I'm suggesting that those two APIs should live together somewhere, ideally alongside FilePath itself.

2 Likes

Proposed interface:

extension CommandLine {
   ...
   public static var executablePath: String? { get }
}

Would it be more useful to have something like this?

 extension CommandLine {

    struct ExecutablePath {
         let absolutePath: String
         let relativePath: String
         let baseName    : String
    }

    public static var executablePath: ExecutablePath? { get }
}

I have been thinking about this some more, especially in the context of potentially invalid unicode paths. What if we change the proposed API to public var executablePathCString: Array<CChar | CWideChar> { get } which gets rid of the invalid representations. Once we get a file path type in the standard libraries we can add the public var executablePath: FilePath { get } spelling.

8 Likes

That's a reasonable middle ground, I think. Those components that want an instance of String can trivially create one from such an array. If this is the direction we end up going, we should consider also adding something like public var CommandLine.argumentCStrings: Array<Array<CChar | CWideChar>> { get } for the same reasons.

(Because Array.span is not back-deployed, but ContiguousArray.span is, and because this API will be back-deployed on Darwin, I do think we'd want to use ContiguousArray here.)

1 Like