SE-0271: Package Manager Resources

In my current project we currently make pretty liberal use of frameworks which we'd love to move to SPM packages and this proposal would alleviate the blocker we have from doing so, so I'm very happy to have this proposal hit evolution. I largely agree with the ideas and give them a +1!

We have similar Bundle extensions in our frameworks such as:

static var components: Bundle {
    class BundleFinder {}
    return Bundle(for: BundleFinder.self)
}

In an attempt to be proactive about testing this out, I renamed these properties to module akin to what SPM would be generating, so it becomes easier to adopt SPM in the near future. However, in a few of our tests, we use @testable to import a couple of frameworks with this property defined and the compiler gives an error of Ambiguous use of 'module' because for some reason they use this Bundle.module property themselves.

When I add a Bundle.module extension in the test target, the compiler is happy because (I figure) that supersedes the ones found in both frameworks (although I realise that will cause issues in this particular instance).

The proposal says the generation of the resources bundle only happens when there is one or more resources to put inside said bundle, so does this mean the Bundle.module extension is only generated when there is a a resource? If so this might cause issues for test targets with no resource files when they import more than one module using @testable that have resources in them.

Should/could the generated Bundle.module property be hidden from @testable imports to prevent test targets from being able to use it?

1 Like

I'm missing an option to customise the build settings.
I would like to use custom CIKernel's written in Metal in a Swift Package.
As explained in https://developer.apple.com/metal/MetalCIKLReference6.pdf in the Xcode Integration chapter, you need to add the -fcikernel compiler flag and a user-defined setting named MTLLINKER_FLAGS set to -cikernel.
With the current state of the proposal, it would not be possible or am I missing something?

Yah, we thought about this at some point and came to the same conclusion.

With this proposal, SwiftPM, the commandline tool will not do anything automatically w.r.t. localization, so NSLocalizedString would not work by default, because the strings files wouldn't be available at runtime. It might be possible to use the copy rule to the get those files in the right place, though, but it is not something we're explicitly designing for here.

IDEs using libSwiftPM could however automatically handle strings files if they choose, too, so e.g. potentially NSLocalizedString could just work when using packages in Xcode.

1 Like

Thanks for trying this out in such detail!

We decided to always generate the resource bundle accessor for test targets back on the pitch thread here.

1 Like

You're not missing anything, I don't think we were aware of the custom CIKernel feature. From the documentation, it sounds like in Xcode today, you have to do this partially via run script phases, is that correct? If that's the case, it sounds as if this would be something that potentially needs the extensible build tools feature pitched here.

1 Like

Overall I love this! A few thoughts:

  1. My main concern right now is one concerning syntax. This syntax doesn't seem to follow the existing Package.swift conventions. .library, .package, .target, etc all use nouns for their declaration. In this proposal, we have .process and .copy, which are verbs.

    A simpler API which more closely matches the other structs could be defined as follows:

    public struct Resource {
        public static func resource(path: String, process: Bool = true) -> Resource
    }
    

    Or, if we think that .copy and .process will grow to other ways in which to handle a resource, those could be defined in an enum:

    public struct Resource {
        public enum Rule {
            case process
            case copy
        }
    
        public static func resource(path: String, rule: Rule = .process) -> Resource
    }
    

    I don't love the naming of Rule, but it matches the existing terminology in the proposal.

  2. In the proposal, the comments around the Resource structure seem to be a bit misleading. One comment states that an instance of a Resource represents an individual resource file. However, the comments around process and copy both say that they can take paths to directories and that the rule will be applied recursively. It might be clearer to note that a Resource can define an individual resource file or a directory containing numerous resource files.

  3. Regarding the following:

    Each file in a target will be required to have a rule. SwiftPM will emit an error if it is not able to automatically determine the rule for a file. For example, if there is a README.md in the target, the package will need to make an explicit decision about this file by either adding to the exclude list or in the resources list.

    This seems to be a bit hostile to the package maintainer. I can envision this frustrating developers by breaking builds whenever a new README file is dropped into the project. Would it be preferable to simply ignore files without implicit or explicit rules? For example, all of the so-called “multi-purpose file types” listed in the proposal would be ignored and NOT be treated as resources by default. Why force the developer to explicitly exclude these when an implicit exclusion can work just as well?

1 Like

One thing that’s still not clear to me: is this also true on Darwin platforms? Will all packages with resources (or whose dependencies may have resources or localisation) have to be built with Xcode on macOS?

First note that this is only for files inside targets, the README file from your example would presumably be dropped in at the top level and would not be a concern.

The big issue with ignoring certain files based on type is that we would force developers to keep in mind a list of file types which are automatically treated as resources and those which are not. This seemed like a huge cognitive burden and even more frustrating considering that the common case for files in targets will be that they are supposed to be either source files or resources.

No, you just need to add flags to the Metal Compiler and Linker:


Both can be added today as Build Settings of a Target in Xcode:

2 Likes

Building packages for Apple platforms other than macOS with commandline SwiftPM is already not supported today. Packages which are using resources which can simply be copied, such as image files, will work in commandline SwiftPM, but we are not proposing to handle file types which require processing such as Storyboards or Asset Catalogs.

That's unfortunate. I'm working on a project that would allow some kind of web templates to be compiled to HTML during Package build. And it would be awesome if SPM supported this kind of dynamic resource transformation feature.

2 Likes

This will be something that needs the extensible build tools feature, pitched here.

Awesome good to know. :slight_smile:

Thanks, good to know!

So the package that Xcode generates for an iOS app (say), would have to specify the compiled asset catalog or compiled storyboards or stringsdict files as Resources rather than the raw, source files?

There seems to me some ambiguity regarding the Bundle.module API. It actually means Bundle.currentModuleAssociatedBundle, although this would mean a much longer name. Any ideas about the improvement of the API naming?

1 Like

The AssociatedBundle is redundant as it‘s implied from the Bundle type. If nothing else then it should be just called currentModule.

The currentModule seems to me that the property refers to a object of type Module. This is sort of violating Cocoa's naming guideline. For instance, NSLocale.currentLocale is definitely of type NSLocale, and the user would be confused if it's another type.

Do you have other similar existing examples in Cocoa/Swift API to support this kind of naming? I really doubt it.

It‘s a 'current module bundle' like you have the 'main bundle' and other similar static properties. Adding type name at the end of the property name to refer to the type the property will return is redundant. I think currentLocale is either just current when used in Swift or it seems like an oversight that is no longer fixable without deprecation of the old entry point.

Terms of Service

Privacy Policy

Cookie Policy