Default Package Swift Settings

It's really common to see Package.swift files that apply the Swift language settings to all targets. Here's a common approach.

// swift-tools-version: 5.9

import PackageDescription

let package = Package(
	name: "MyPackage",
	products: [
		.library(name: "MyPackage", targets: ["MyPackage"]),
	],
	dependencies: [],
	targets: [
		.target(name: "MyPackage"),
	]
)

let swiftSettings: [SwiftSetting] = [
	.enableExperimentalFeature("StrictConcurrency"),
	.enableUpcomingFeature("DisableOutwardActorInference"),
]

for target in package.targets {
	var settings = target.swiftSettings ?? []
	settings.append(contentsOf: swiftSettings)
	target.swiftSettings = settings
}

I'd like to propose adding a new top-level property to the Package structure that makes it easier to define default settings for all Swift targets. Per-target settings would be merged with higher precedence. I'm also proposing this specifically to Swift and not C/C++, because I think this is by far the most-common case. But, if there are strong feelings here, I'm definitely open to expanding the idea.

Here's the new proposed manifest:

// swift-tools-version: 5.9

import PackageDescription

let package = Package(
	name: "MyPackage",
	products: [
		.library(name: "MyPackage", targets: ["MyPackage"]),
	],
	// new stuff goes here!
	swiftSettings: [
		.enableExperimentalFeature("StrictConcurrency"),
		.enableUpcomingFeature("DisableOutwardActorInference"),
	],
	dependencies: [],
	targets: [
		.target(name: "MyPackage"),
	]
)

What do you think?

12 Likes

Very reasonable, but I think I would like to call it defaultSwiftSettings (like your title suggests)

2 Likes

I think the pitch makes sense at a high-level.

I think this will need a lot more concrete design, potentially with different merging strategies based on the type of settings. For example, Xcode has a pretty sophisticated model here, with multi- and single-value settings, $(inherited) etc -- a lot of the cases that solves will likely come up in a multi-layer build settings model for SwiftPM as well.

There's also the question of how to address potential ambiguity with swiftLanguageVersion (see SE-0435) since that would also be part of SwiftSetting now, but there's an existing package level property for this already.

2 Likes

I thought about this! I opted for more straight-forward spelling because I think the risk for ambigitity/confusion is small. But I'm definitely open to it!

You are absolutely right! I didn't even know a multi-layer settings model existed. Where can I learn more about it?

Shoot. Am I correct that there is currently no validation on the strings that go into these settings vs the active language model? And if I am, I'm inclined to use that as precedent to say that, just like there, you have to be equally careful.

I would want to see a similar treatment for C and C++ compiler settings. :slight_smile:

5 Likes

fine...

:wink:

I think I was a bit unclear about my concern. Basically if we take your pitch and SE-0435, it will be possible to write something like this:

let package = Package(
    name: "pkg",
    ...
    swiftSettings: [
        .swiftLanguageVersion(.v6),
    ],
    ...
    swiftLanguageVersions: [.v5]
)

I think that is pretty ambiguous.

4 Likes

That is confusing! Am I correct that this would be equivalent to:

let package = Package(
    name: "pkg",
    ...
    targets: [
        .target(
            name: "target",
            swiftSettings: [.swiftVersion(.v6)]
        ),
    ],
    ...
    swiftLanguageVersions: [.v5]
)

Could this handle the situation in the same way?