As expected, Swift uses the compile time value to populate #file special literals, however this means my customers are seeing my local project directory printed when my app crashes due to a fatalError() call, e.g:
Fatal error: BOOM: file /Users/ian/code/ileitch/periphery/PeripheryCmd/Code/main.swift, line 10
I think this is confusing for some customers to see as it's not a directory on their local machine. Obviously I can switch to a different directory for release builds, but I was wondering if there's a compiler flag to alter the default value? Ideally I'd like it to just output only the name of the swift file, or a relative path.
AFAICT, this is more of an Xcode problem than a Swift problem. It looks like the path that gets embedded for #file is the exact path to the source file that gets passed on the command line to swiftc. If you invoke swiftc Foo.swift, then #file == "Foo.swift", and if you invoke swiftc ./Foo.swift, then #file == "./Foo.swift".
The reason you're seeing the full paths is because Xcode passes them that way to the compiler. I don't see a setting to control this behavior, but I could be missing something.
I wonder if it would make sense then for SwiftPM to not pass absolute paths, but rather update the working directory of the process before invoking the compiler.
Can you think of a situation where you would want paths returned by #file to look different than the paths embedded in your binary's debug symbols?
If the answers to these are "yes" and "no", respectively, then I'd propose that debug path remapping could also solve the same problem for #file. But I'd also like to hear if there are reasons we shouldn't do that that I haven't considered.
Add a native lastPathComponent in Swift Foundation for String
Add a new #fileName literal
Use #file but have it detect optimized builds where asserts cannot fire and produce a lopped string when these conditions hold, otherwise the existing behavior of #file
Retool Xcode and let Swift and Swift Foundation be.
#fileName sounds great, but it doesn't really solve my issue, since fatalError still uses #file. Also in general I think I'd like all uses of #file to be altered, vs. having to change every use to #fileName (which would be unreasonable given that 3rd party dependencies may be using #file).
My favored solution is a compiler argument that alters #file output relative to a given project path.
I agree that a relative path would be better than just the file name. Passing the path it should be relative to to the compiler and having it fall back to absolute paths sounds reasonable to me.
The only thing where this might bite us later could be a constexpr/macro/whatever model where finding the path to the current file would then become a little more cumbersome (either have to get the base directory as well or make #file behave a little different in that context).
Adding more literals with slightly different path components doesn't feel like the right solution here. It increases the API surface of the language and it makes assumptions that the basename of the file is the only part that matters, but that doesn't have to be the case; someone could have their sources organized in directories with the same basename appearing in multiple directories, even in the same module.
I'm not sure your reply to my post addressed the questions I had, so I'll add some more information to clarify. If we add support to swiftc for a feature like Clang's -fdebug-prefix-map, we'll be able to remap paths in debug info in the following way, for example:
You have a file at /Users/username/My Code/Swift/MyCoolProject/Sources/MyClass.swift.
You compile it with swiftc ... -debug-prefix-map="/Users/username/My Code/Swift/"= ... (remapping the path parts you don't want exposed to the empty string).
Now the debug info embedded in your binary/dSYMs would have the file path MyCoolProject/Sources/MyClass.swift instead of the absolute paths.
I'm suggesting in this thread that we could apply the same remapping to #file as well and that would be suitable for most people's purposes, assuming that people don't wish for their #file strings to be different than the paths in the debug symbols.
What I'm trying to figure out is whether that's a reasonable assumption.
Using #fileName instead of #file for fatalError, XCTAssert, etc. is not a good solution. We might have two files dirA/C.swift and dirB/C.swift -- we would want to be able to tell where the error occurred.
I don't think there's a sound way around using paths relative to the project root.
There's not always a good notion of "project root" either. A SwiftPM package always has all the source files in one place, but an Xcode project may have them strewn across multiple directories. That said, an 80% solution might be good enough here. (We could also have a warning if #file is ever used with a file that's not relative to whatever's specified as the "project root".)
Might it be reasonable to have project root be specified as a compiler flag, and #file will emit paths relative to this path? If the project is poorly organized you get things like the following, but that should be considered a user "error", and not something for the compiler to try and cope with on its own.
This is precisely what I suggested above about reüsing -debug-prefix-map if it's accepted. That would kill two birds with one stone, if the assumption holds that people would want the same #file strings that also show up in their binaries' debug symbols.