How to disable implicit Foundation imports?

Right on Darwin platforms Foundation gets automatically linked. I actually never dove into where and how this gets done. Building the same package on Linux should produce a compile error that you are not importing Foundation

But the correct auto-link behavior is for Darwin target instead of host. There should be some bug here… Can someone replicate the bug with it?

I don't think it will, according to documentation trimmingCharacters(in:) is declared in the Swift standard library.

Oh right. Sorry I was just checking this from my mobile and assumed it was a Foundation API. Disregard my comments then

This is really… Shocking.

It turns out that CharacterSet is a Foundation type, that should be why Foundation is required and linked.

https://developer.apple.com/documentation/foundation/characterset
https://swiftinit.org/reference/foundation/characterset

1 Like

Which documentation? The stdlib code doesn't show this method:
Search · trimmingCharacters · GitHub

This documentation refers to NSString: Apple Developer Documentation

1 Like

I'm referring to the function available on StringProtocol, not the one on NSString.

https://swiftinit.org/reference/swift/stringprotocol.trimmingcharacters(in:)?from=swift-core-libraries

It is an extension from Foundation. This is definitely ruining the rule of not extending a type you don’t own (I believe Foundation adds a bunch of extensions to String, but additions to StringProtocol is even worse).

Perhaps from the very beginning Apple engineers thought they own both, but that’s incorrect now. And unfortunately I suppose fixing it is ABI breaking.

P.S. Definitely wants to give @taylorswift a credit here since swiftinit has proved to be more precise and useful than the official documentation… It saves a lot.

4 Likes

I hope there's a way to add a requirement for import Foundation when using protocol extensions declared in Foundation as a source-breaking, not ABI-breaking change. In my understanding, this shouldn't change how the linker works, or what binary code is emitted.

Isn't this how it normally works for other modules provided via SwiftPM?

thank you so much!!

3 Likes

I guess there’s something wrong to make the code compile. Even on macOS where Foundation is automatically linked, Foundation.swiftmodule won’t be pulled in without manual import, so the code shouldn’t compile.

I am as confused as anyone by this. But I keep coming back to the finding that adding import JavaScriptKit to any file in the module causes Foundation to be linked implicitly. Even for Darwin with a plain swift build and even though JavaScriptKit itself has not even a single mention of the word Foundation in the entire repo. I will investigate it further though – maybe it's importing something which has a transitive dependency on Foundation

dependencies: [
    .package(url: "https://github.com/swiftwasm/JavaScriptKit", from: "0.15.0"),
],

is needed to import JavaScriptKit

To be precise, Foundation is unexpectedly imported. We should check the compiler and importer first. I don’t think the linker flags makes sense given that we’re seeing symbols from Foundation leaked into the code.

I have tracked this down to a simple reproduction. In short, it appears to be a bug in SwiftPM with bundled resources.

edit: I removed the original post, which outlined the contents of the reproduction in text form and posted the minimal reproduction as a SwiftPM project to GitHub - ephemer/swiftpm-implicit-foundation-imports-reproduction

Yet another edit: so my workaround for now will be to fork JavaScriptKit and remove the resources: line of Package.swift

1 Like

+1 to this, -lFoundation is probably always passed, but the toolchain (or the linker specifically) is not linking with that static library if no symbol is referencing it.

@Geordie_J Which toolchain did you use that has the bug? I can’t replicate it locally.

EDIT: Okay I got it. You may want to place a .gitkeep file under Sources/Dep/Runtime to let Git recognize the directory.

1 Like

Great catch! The issue is with resources and SwiftPM then. As soon as resources are specified in Package.swift, SwiftPM generates a file for referencing this resource through the Bundle type, which is declared in Foundation.

If you're using carton, excluding JSKit resources may cause issues though, because we're relying on SwiftPM resources to make runtime files discoverable.

Seems like SPM will always create such accessor file that depends on Foundation.Bundle if we have at lease one resource file(a bundle exists)

Should we consider adding a flag to control this?

1 Like

What would be the point of bundling resources that can't be used? Bundle.module is the only supported way to access resources.

The downstream user who is importing the dependency may be using only a sliver of the library's functionality and not need or know about the bundled resource; that Foundation APIs are imported should (IMO) remain under explicit user control. Put another way, if the bundled resources aren't being used, better to bundle just resources that can't be used than both resources and all of Foundation.

3 Likes