We need `#fileName`


(Dave DeLong) #7

Hm, I really like that idea. A compiler flag to change what #file gets expanded to. Default expansion for source compatibility would be to expand to the full path.

SWIFT_FILE_EXPANSION_TYPE =                 // ex: /Users/dave/Code/Project/Target/file.swift
SWIFT_FILE_EXPANSION_TYPE = absolute        // ex: /Users/dave/Code/Project/Target/file.swift
SWIFT_FILE_EXPANSION_TYPE = relative        // ex: Target/file.swift (relative to SRC_ROOT)
SWIFT_FILE_EXPANSION_TYPE = name            // ex: file.swift (last path component)
SWIFT_FILE_EXPANSION_TYPE = none            // ex: (empty string)

Compiler flag name can easily be bikeshedded.


(Pyry Jahkola) #8

The last time I checked #file was indeed relative whenever the command-line argument to the Swift compiler was relative. Unfortunately Xcode never had an option to use relative paths when building projects.


(Josh Caswell) #9

basename (actual name up for bikeshedding) would be good to include as well: no path and no extension, since extension is fairly redundant.


(Tony Allevato) #10

Swift 5.0 will include the -debug-prefix-map flag that was added to remap absolute paths encoded in debug info, because build systems like Bazel rely on being able to cache build artifacts from remote machines whose workspaces may not have the same absolute paths (and then also debug them on entirely different machines).

I think it would make a lot of sense to reuse that flag and extend that remapping to #file as well.


(Jens Ayton) #11

Can we have this for Clang too plz. :slight_smile:


(Daniel Höpfl) #12

I like the idea.

Regarding privacy concerns:

SWIFT_FILE_EXPANSION_TYPE = hashed     // ex: Target/file.swift -> 880b724b5553f4182100e9c2402bc1d33f4924af

(Joe Groff) #13

To me, it makes sense to change the behavior of #file rather than add a new #fileName modifier, since for all the reasons the original post notes, the full path is rarely desirable. With the help of build systems telling the compiler what the root directory of a build is, it'd be great for it to provide just a relative path from that root as the standard behavior. Absent any compiler flag, the directory containing the current source file strikes me as a safer default root than the current behavior as well.


(Dave DeLong) #14

Hm, I could see the use for hashing, but I think it'd deserve to be a separate compiler flag, so I could hash the full path, the relative path, the file name, or the base name.

SWIFT_FILE_EXPANSION_TYPE = relative
SWIFT_FILE_HASH_ALGORITHM = none // default. also: sha1, sha256, md5, etc

Thoughts?


(Tony Allevato) #15

I think we might be approaching feature creep here. In the majority of cases such fine control isn't necessary, so we don't need to overcomplicate the feature for every niche case. Particularly, if you're concerned about the privacy of your source file paths to the point that even the basename is something you want to hash instead, then you ought to be using #if guards to not ship the strings in the first place.

In addition to the existing -debug-prefix-map flag mentioned above, another possibility would be to use the existing -working-directory flag and stem the paths based on that. So if you have the file /confidential/path/to/my/app/Sources/App.swift and you pass -working-directory /confidential/path/to/my/app, then #file would be "Sources/App.swift".

That would jibe nicely with @Joe_Groff's suggestion of falling back to the CWD absent any other flag, because (I think) the driver internally uses CWD if -working-directory is not provided (correct me if I'm wrong).


(Tony Allevato) #16

In fact, when invoking swiftc directly, #file appears to just be whatever exact path you passed to it:

// main.swift
print(#file)
$ swiftc main.swift && ./main
main.swift

$ swiftc ./main.swift && ./main
./main.swift

$ swiftc $PWD/main.swift && ./main
/Users/myusername/path/to/main.swift

(Joe Groff) #17

Yeah, the hashing use case strikes me as stretching the purpose of this language feature, and also a potential security hazard if used with hash tables that are also exposed to user input, since the supposedly "random" unique hashed IDs are being seeded with predictable strings.


(Nick Lockwood) #18

I do actually use the full #file path in a couple of projects. My use case is locating the source directory from inside a running an app in order to monitor and hot-load resource file changes on the fly.

I also pretty regularly use #file inside test targets to refer to source or resource files at relative locations inside the project.


(Jeremy David Giesbrecht) #19

I also make heavy use of it during development. Because the package manager and Xcode have different ideas about what the working directory for unit tests should be, it is the only way I know to reliably find the package directory in both contexts in order to load/export Git‐tracked specifications.

There is a lot of package testing code out there that relies on things like this:

let packageRoot = URL(fileURLWithPath: #file)
    .deletingLastPathComponent()
    .deletingLastPathComponent()
    .deletingLastPathComponent()

let resources = packageRoot.appendingPathComponent("Resources")

However, I agree that there is probably never a good reason to have full paths still kicking around in a binary that will be shipped without its source. fatalError() and friends are over‐eager about it and also hide what they are doing.

Something should be done, but completely removing the ability to discover the full path even during development would be crippling.


(Joe Groff) #20

If we were going to make #file relative to a given source root, then maybe we could provide that #sourceRoot as a special value too. Would that work for your use cases?


(Jeremy David Giesbrecht) #22

Yes. With #sourceRoot too, all the use cases I can think of would still be possible. I like that idea much better than the status quo.


(Adrian Zubarev) #23

Without derailing from the main topic, with all these new keywords being pitched I think the context is growing too fast. Would it make sense to generalize that into a special StaticContext<T> type where the generic parameter allows the users to specify the subset of all #keywords, so that the context value isn't exploding in size where not appropriate? This will also reduce the number of parameters a function must have to obtain such static context compared to current status quo.

There was a similar topic, but instead of smashing everything inside one type it would be far better if we could specify a subset of static information we want to obtain:


(Joe Groff) #24

I agree, that would be a good idea, especially since people are in fact relying on the current behavior of #file and we don't want to disrupt them, so that suggests to me introducing new context values would be preferable to breaking the existing ones.


(Matthew Johnson) #25

I really like the idea of StaticContext. It's much more usable than an ad-hoc collection of parameters that all have to be forwarded individually.


(Adrian Zubarev) #26

Aside the pitch from @davedelong, I tried to create my own type to accumulate the context but I failed because the current static capturing behavior isn't that trivial as I initially thought.

So it definitely requires a formal proposal and some compiler help to create such a type.


(Dave DeLong) #27

IIRC @brentdax has also taken a stab at implementing #context; I do not know how far he's gotten.