Package Manager Conditional Target Dependencies

Hello,

I've been working on a proposal to allow assigning build environment conditions (with the same syntax as for build settings) to target dependencies in the Package Manager manifest to allow setting under which platform or configuration conditions the dependency should be observed.

An implementation is already available if you want to play with it, and some toolchains are available to make that easy:

Please let me know what you think.

https://github.com/hartbit/swift-evolution/blob/conditional-target-dependencies/proposals/NNNN-swiftpm-conditional-target-dependencies.md

19 Likes

I think this looks good! I look forward to seeing it make its way into SwiftPM.

Very nice.

Just to make sure that I understand this correctly - does this also allow for conditional targets, if you group them under an umbrella target?

Let's take the example from the proposal, with two dependencies - bluetooth and bluetoothlinux. Imagine that these libraries vend a common API, and the author would like to merge the repositories. I'm guessing they would be able to do something like this:

import PackageDescription

let package = Package(
    name: "Bluetooth",
    products: [ .library(name: "Bluetooth", targets: ["Bluetooth"]) ]
    targets: [
        .target(name: "BT-mac", path: "Sources/backends/mac"),
        .target(name: "BT-linux", path: "Sources/backends/linux"),
        .target(name: "Bluetooth",
            dependencies: [
                .product(name: "BT-mac", condition: .when(platforms: [.macOS])),
                .product(name: "BT-linux", condition: .when(platforms: [.linux])),
            ]
        )
     ]]
)

And then, within the Bluetooth module, they can use #if canImport(BT-mac) and #if canImport(BT-linux) to implement their unifying abstraction.

And downstream users of the package will be able to write import Bluetooth and that will "just work" when compiling for either mac or linux?

2 Likes

This looks great! It's exciting to see lots of progress on SPM lately.

Thanks! It's great to see this proposal.

How are macOS vs iOS vs Catalyst distinguished?

Great proposal!

This may be deferred to other proposal but I think we should expand the BuildSettingCondition API:

  • Add platform families: for example, the Apple Platforms family .darwin/.apple
  • Add the equivalent to targetEnvironment on Swift code with similar options: .simulator/.macCatalyst.

Looks great! I wonder if we should also add ability to conditionalize based on the target triple. That will provide a lot of flexibility and an escape hatch for the platforms (and other information in the triple) SwiftPM doesn’t know about yet. This could also be proposed separately but we might as well consider it while we’re on the topic.

2 Likes

I think that will work, but it will require adding @_exported declarations in the Bluetooth module to avoid having to import BT-mac and BT-linux from client packages.

There are .macOS and .iOS platforms. Not sure about Catalyst. I might not be the best person to answer that question.

Yeah, we can chat about it, but I agree it should probably be discussed separately as it will then affect both build settings and target dependencies.

How would you see users configure that condition? With a string?

If you want to play with the implementation here are toolchains:

yep

It's not currently possible to distinguish Mac Catalyst from the other two -- it probably should be, though.

One thing I would like is to have optional system libraries, rather than listing out all of the individual platforms on every single target, and then to add a condition like .when(available: "CoreGraphics"). I really, really dislike the whole iOS, tvOS, watchOS dance. I don't expect that list to get shorter in future, either.

Anyway, the reason why it's relevant to this proposal is that it would possibly mean using a different kind of condition to BuildSettingCondition - more like a TargetAvailabilityCondition.

EDIT (aside): I'd love to test the toolchain, but apparently you can't be trusted, and there doesn't seem to be any way for me to overrule it. The button to not require notarisation in system preferences has gone :man_shrugging:. As a long-time Mac user - this is, to be completely honest, very sad.

1 Like

Perhaps I'm missing the point, but I don't think this is possible, because SwiftPM can't know at compile time which libraries are importable at runtime on different platforms.

Just move the toolchain into /Library/Developer/Toolchains/ and launch Xcode.

If you get the warning, you can always allow it from the Security & Privacy preferences, on the General tab, towards the bottom:

It would be based on what's available in the SDK you're building against.

Yeah that's what I tried first, but the new API requires tools version 5.3, which Xcode refuses to work with, even with the new toolchain selected ("/.../Package.swift: package at '...' is using Swift tools version 5.3.0 but the installed version is 5.1.0")

Tried that already, but thanks.

You might want to run swift build directly from the command line. To have the CLI select this toolchain, define the following environment variable. To check that it works, check the output of xcrub --find swift:

$ export TOOLCHAINS=org.swift.pr.28041.441
$ xcrun --find swift
/Library/Developer/Toolchains/swift-PR-28041-441.xctoolchain/usr/bin/swift

Then, when you run swift build, it should popup a different interface that WILL allow you to accept the consequences of trusting me ;)

Nice try - the export line does at least ensure the correct CLI tools are selected (switching in Xcode doesn't seem to do anything to the CLI anymore). I have no reason to doubt your trustworthiness, but apparently Apple does, so... who am I to argue?

I can't imagine this is the first time the issue pops up. Do you all run secret builds with the safety wheels off or something?

(Oh, and I already tried the standard method of opening the executables from the Finder's right-click menu. Seriously, Apple is pretty insistent something sketchy is going on - what did you do??)

Now, you should be able to bypass the restriction from the Security preference. I don't know why it's so difficult to run a custom toolchain :frowning: @mishal_shah Any ideas if we are doing something wrong?

I wish. Rebooted, did everything again, still no luck. I think I'm just going to spin up a Linux VM.

It's (frankly) completely ridiculous, but macOS absolutely refuses to allow me to run this program. Is anybody else (on Catalina 10.15.1) able to run the toolchain?

Have you tried remove the quarantine attribute from the toolchain?

$ xattr -d -r com.apple.quarantine /path/to.xctoolchain

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

5 Likes