[Pitch] Package traits

Thanks for expanding. I think there a few assumptions in your example that we have to call out. In your setup, the SQLCipher traits switches the used SQL implementation for every user of the GRDB package to use the just built SQLCipher instead of the system sqlite one. This is one way to make this work but as you correctly pointed out if the system sqlite ships new features then one module might break since they suddenly use the SQLCiphher based implementation instead of the system provided one.

Now I agree this is an invalid use of traits and I wouldn't recommend to set it up this way. Instead I would propose that GRDB provides two different internal transports one for the system SQLite and one for SQLCipher. So that inside an application you can instantiate two different database instances each using either the system SQLite or SQLCipher. Something along this way:

let databaseUsingSystemSQLite = Database(transport: .systemSQLite)
let databaseUsingSQLCipher = Database(transport: .sqlCipher)

Now this approach might have other complexities such as setting up the build correctly that GRDB can use the symbols either from the system or from SQLCipher but this would make GRDB more versatile and wouldn't lock it to a single source of SQL. At this time I don't know if this is possible to do but this is how I am thinking about different networking stacks for SwiftNIO or different transports for OpenAPI.

I see the confusion. Let me clarify. A package must support building with all traits enabled as long as all system dependencies are present. It is totally acceptable to use a trait to guard the usage of a system dependency and fail the build if the dependency isn't there.

1 Like

This is not possible, unfortunately, because SQLite and SQLCipher can't be both linked :sweat_smile: They use the same C api, the same C symbols, etc. Choosing between SQLite and SQLCipher must be done globally, at the linker level. It can't be done at runtime.

1 Like

Is this really true? You surely can rename the symbol names of SQLCipher to support a binary that wants to use both. We have been vending our own C libraries often in the server ecosystem by just renaming the symbols e.g. we do this for BoringSSL which a lot of server applications ship at least two copies of.

In the end, I don't see this as a trait problem per-se but a general problem with conflicting modules/symbols in a build graph.

1 Like

I'm happy BoringSSL supports symbol prefixing. SQLite offers no such convenience, though. I'm not paid full time working on GRDB so your suggestion is not, practically, applicable.

We're not discussing a real problem, anyway, because SPM is not currently able to build SQLite or SQLCipher (I have pasted some links above).

I think a good direction here would be to offer a single command to do pre-submission checks, e.g. CocoaPods provides this with their lint command. This could perform traits related checks, documentation generation, integration as a dependency etc.

This is obviously not directly related to traits, but IMHO guiding developers to write good and functioning packages is a core responsibility for a package manager and shouldn't be relegated to separate community efforts.

8 Likes

One thing i initially assumed this pitch would allow is handling different "variants"

For instance having a library switch between environments (development staging production)
Another example use-case we have in our company.

The unification of traits does not really support this.

I know there are some use-cases where unifying traits is desirable but im wondering if there is some way to acommodate this as well.

By introducing a trait that allows definining multiple mutually exclusive variants
Something along the lines of this

        Trait(
             name: "environment",
             values: [
                 "development",
                 "staging",
                 "production",
             ],
             default: "production"
         )

3 Likes

That's not this pitch. Which can be evaluated on its own, because it has merits! But yeah we're having other needs.

Interesting proposal! Another small point to consider is whether package traits will be exposed via SwiftPM’s dump-package command.

I'm not sure whether it would make sense for SPI to report on the specific traits that a package author declares, but if they were echoed out during a dump then we'd have the option to list them, or at the very least flag that a package has traits.

Adding an optional description to indicate each trait’s purpose might also be good. If that became widely adopted, the descriptions of traits might make more sense to expose via package metadata on SPI.

2 Likes

Thanks for bringing this up! I agree we should surface the traits in the dump-package and I love the idea of adding an optional description to allow package authors to explain the individual traits in detail!

4 Likes