How to use a custom build configuration in a Swift Package?

How can I do something like this:

    .target(
        // some other settings ...    
        swiftSettings: [.define("ENABLE_TESTABILITY=YES", .when(configuration: .testing))]
    )

We use a custom configuration called "Testing" (instead of the default "Debug" or "Release") when conducting unit tests.

However, Swift packages only seem to know about .debug and .release configurations.

As a result this package's own tests cannot @testable import it when it's built and tested as part of a Scheme that defines "Testing" as the build configuration to use when building tests.

Packages don't support custom build configurations.

Some replies to this earlier post seemed to indicate that Xcode 11.3 had added support for custom build configs to Swift packages: Swift package manager and custom build configurations

If that's wrong and indeed there is still no way to add another config besides Debug or Release, do we have a timeline on when this would be added? This could be deal-breaker for us... the only workaround I've seen, requires the bundle name to be different based on the configuration.

Maybe we can add a check for a compile-time environment variable or something.

Like maybe it would work in our .xcconfig files to have:

GCC_PREPROCESSOR_DEFINITIONS = TESTING=1

Then in Package.swift:

#if TESTING
// define target including "ENABLE_TESTABILITY=YES"
#else
// don't
#end

Question is, when Package.swift is compiled, will config-specific GCC flags have been set yet?

Prior to 11.3, there were issues when using packages with Xcode targets that were using custom configurations. Those have been fixed, but nothing has changed in regards to being able to conditionalize based on custom configurations.

Packages do not inherit build settings from projects in any way.

Has anyone made a proposal to implement support for alternate configs?

Seems like it would suffice to simply have a third option called .custom("MyConfigName") in addition to .debug and .release.

This seems more like a workaround than a solution to me, because now clients of that package need to use "MyConfigName" to get that behaviour. What if I am using two packages with different custom configurations that I need to use? I think this really only works if the packages and the client are tightly coupled, e.g. when you are using packages to break up a larger app.

What would be better is something like "Package Features" idea that Daniel mentioned in My SwiftPM wishlist (aka proposal proposals), a way to opt in to building the package with additional build settings that is independent of the build configuration and can be selected on a per-package basis.

The same argument applies to the use of "Debug" and "Release" configurations though.

These are merely arbitrary labels subject to change by the user. There's no guarantee someone will have a "Debug" or a "Release" configuration nor is there any objective reason why they should be required to have one.

At least half the projects I've worked on in my career changed these to other things, such as "Dev," "Staging," and "Distro" etc. It's also common for a specific team within the org with their own CI to use special configs that only they and their CI use, to insulate the production CI pipeline from whatever they're doing.

In our case we're simply using Packages to create modules to add new modules to our app (in the past we always used Frameworks but we are hoping to use Swift packages now since it's so much lighter-weight than creating a whole framework, and nobody likes dealing with merge conflicts in Xcode project files when multiple devs are moving files around).

The inability to specify the name of our existing build configurations is really crippling for using Swift packages within our app. I don't understand why simply letting the user specify whatever configuration name they want would be a big deal. We use a monorepo so there's no sense in which "package management" even comes into play for us, at least not for our app's own modules.

If you're making a Swift Package for distribution, and the concern is that people won't want to add configurations with the names specified by the package, then I agree with you, we shouldn't use "the name of which configuration is active" as a criteria in the first place.

What would be better is something like "Package Features" idea that Daniel mentioned in My SwiftPM wishlist (aka proposal proposals),

100% agreed here.

Like maybe a package could define its own build-time flags like "MyPackageName_Testing", and the BuildConfigurationCondition would be contingent on this flag being set.

I think there is a small misconception, because the model for packages is slightly different from the one for Xcode targets you are describing here. Packages always have release and debug configurations, there is no way to rename or remove them. When building in Xcode, one of those two gets picked, based on heuristics (e.g. if the scheme uses "Development", debug gets used).

Are you primarily asking for a new feature, or asking how to best update your existing set‐up?

If the second, what exactly are you trying to accomplish?

The package’s own tests can @testable import its modules without any extra set‐up. Debug builds have testing enabled. If it isn’t working for you, what is the error message you’re getting?

Outside Xcode, the manifest inherits the terminal’s environment, so you can do stuff like this if necessary, as long as you stick to the command line (or use it to generate your Xcode project).

It may help ease the transition of your workflow, but consider it a last resort. Always prefer using directly supported methods instead wherever possible.

the model for packages is slightly different from the one for Xcode targets you are describing here. Packages always have release and debug configurations, there is no way to rename or remove them

If that's the case then a Swift Package should not derive it's build configuration from the active Xcode configuration in any way.

Instead, each Xcode configuration should independently define whether to use a "Debug Package-build-configuration" or a "Release Package-build-configuration".

Because I'm not the one who's confusing these two concepts. They're confused in the design itself.

The problem is, we have a scheme called "AllTests" which builds and runs all the various test targets of our modules.

If I just build the Package by itself, then its tests are able to use @testable import just fine.

However if I build the package as part of this other scheme, then it no longer works.

To get around the currently insufficient implementation of "conditionality based on build configuration", the workaround I came up with is to define a second, identical .target in Package.swift, which is named MyPackage_Testing. I then setup Sources/MyPackage_Testing as a symlink to Sources/MyPackage.

The tests will @import MyPackage_Testing. Then in the .target definition for MyPackage_Testing I defined swiftSettings: [ .define("ENABLE_TESTABILITY") ].

Kind of hacky but it works.

Terms of Service

Privacy Policy

Cookie Policy