Foundation forced linking in SwiftPM with no "import Foundation", other imports or dependencies

Adding resources to targets in Package.swift leads to generating resource_bundle_accessor.swift which imports Foundation even if there is no "import Foundation", other imports or dependencies.

// resource_bundle_accessor.swift
import class Foundation.Bundle
import class Foundation.ProcessInfo
import struct Foundation.URL

private class BundleFinder {}

extension Foundation.Bundle {
    /// Returns the resource bundle associated with the current Swift module.
    static let module: Bundle = {
        let bundleName = "<name>_<target>"

        let overrides: [URL]
        #if DEBUG
        // The 'PACKAGE_RESOURCE_BUNDLE_PATH' name is preferred since the expected value is a path. The
        // check for 'PACKAGE_RESOURCE_BUNDLE_URL' will be removed when all clients have switched over.
        // This removal is tracked by rdar://107766372.
        if let override = ProcessInfo.processInfo.environment["PACKAGE_RESOURCE_BUNDLE_PATH"]
                       ?? ProcessInfo.processInfo.environment["PACKAGE_RESOURCE_BUNDLE_URL"] {
            overrides = [URL(fileURLWithPath: override)]
        } else {
            overrides = []
        }
        #else
        overrides = []
        #endif

        let candidates = overrides + [
            // Bundle should be present here when the package is linked into an App.
            Bundle.main.resourceURL,

            // Bundle should be present here when the package is linked into a framework.
            Bundle(for: BundleFinder.self).resourceURL,

            // For command-line tools.
            Bundle.main.bundleURL,
        ]

        for candidate in candidates {
            let bundlePath = candidate?.appendingPathComponent(bundleName + ".bundle")
            if let bundle = bundlePath.flatMap(Bundle.init(url:)) {
                return bundle
            }
        }
        fatalError("unable to find bundle named <name>_<target>")
    }()
}

I'm developing cross-platform library for Linux, Android and Apple OSes which needs nothing from Foundation. Wanted to add resources in SwiftPM in the way that structure of Package.swift expects.

I faced the issue when I was trying to launch swift target on Android that has defined resources in Package.swift, it crashes on launch due to lack of Foundation dependencies (libswiftDispatch.so, libFoundationInternationalization.so, libFoundationEssentials.so, libFoundation.so, lib_FoundationICU.so). So I need to pack all this dependencies to my .apk, even if I use nothing from Foundation.

Why I don't want Foundation linking:

  • no need any code from it. Maybe only Bundle.module.bundlePath, where I can extract bundle name. Even this code is not from Foundation, it is from generated resource_bundle_accessor.swift file;
  • my .apk size for aarch64 went from 6.3 mb to 27.9 mb when all Foundation libraries are included. Also it went from 18.1 mb to 83.5 mb when bundling .apk for all three targets: aarch64, armv7, x86_64.
  • due to specific character of resource_bundle_accessor.swift to Apple platforms, it crashes in runtime on other platforms;

Ideas for alleged proposals:

  • cross-platform dependency-less code in resource_bundle_accessor.swift, for example something like this:
public struct ResourcesBundle {
   public static let bundleName = "<name>_<target>" // as it is now
}
  • search for resource_bundle_accessor.swift in target sources where resources arguments are defined, if not found use default variation;
  • pass path to custom .swift file for resources definition as argument to -Xbuild-tools-swiftc, if argument isn't defined use default variation;

Unfortunately, the Bundle class is defined in the old Foundation module, so there's no way around importing all of Foundation if you want to use it right now. @itingliu, has there been any consideration to move Bundles to the smaller FoundationEssentials module?

In the meantime, I think you're stuck writing your own code to access such runtime resources, @purpln, if you're trying to avoid using Foundation.

For me there is no logic when you define resourses in SwiftPM, it automatically imports Foundation via resource_bundle_accessor.swift, which crashes, because it's need to be platform specific. So FoundationEssentials is not a solution. Resource .swift file need to be defined by developer, because there could be many platform unique things. There could be different OSes, different bundling solutions. Also it's not obvious at all when some hidden file is generated with hidden dependencies, I think it must be as general as possible.

Sure, that SwiftPM resource option may be broken on all non-Darwin platforms, though I'm surprised it doesn't work for you on linux, unless it's only because you're not using Foundation there either.

I just linked you the generated source, it's not hard to find. If you think it needs to be fixed or extended, that github repo is where you can file an issue or submit a pull. SwiftPM is open source, just like the rest of the linux toolchain, so you can contribute to make it what you'd like it to be. :smiley:

1 Like