Xcode builds build tools for the target, not the host?

I'm trying to use the new build tool support in Swift 5.6 for an iOS project. I have a package which defines an executable, a plugin which runs that executable to generate some swift sources, and a library that builds those sources and depends on the plugin:

let package = Package(
    // ...
    products: [
        .plugin(
            name: "XUIComponentsBuildTool",
            targets: ["XUIComponentsBuildTool"]
        ),
        .executable(
            name: "XUIComponentsGenerateSources",
            targets: ["XUIComponentsGenerateSources"]
        ),
        .library(name: "XUIComponents", targets: ["XUIComponents"]),
    // ...
    targets: [
       .plugin(
            name: "XUIComponentsBuildTool",
            capability: .buildTool(),
            dependencies: ["XUIComponentsGenerateSources"]
        ),
        .executableTarget(
            name: "XUIComponentsGenerateSources"
        ),
        .target(
            name: "XUIComponents",
            // ...
            plugins: ["XUIComponentsBuildTool"]
    // ...

This all works so long as I'm targeting macOS, but I want to consume this library from an iOS app built with Xcode. First, Xcode seems to build the tool twice, once for arm64 and once for x86_64?

And as you can see, both are built for the iPhone simulator, instead of for macOS. Then (obviously) when it comes to run the tool, it can't:

Have I just configured something wrong, or have I hit a genuine bug in libSwiftPM or Xcode? Does anyone have any ideas for workarounds?

2 Likes

Thanks a lot for the detailed bug report here. Would you mind filing a bug with Apple as well (referencing this thread for the details if you don't want to repeat them in the bug report)?

I've submitted this as FB9979213

Thank you very much!

Running into the same issue as well, except since my build tool depends on swift-argument-parser, it fails to build since swift-argument-parser doesn’t build for iOS.

I had the same issue.
I managed to work around it by packaging the plugins dependencies as a artifact bundle as described here swift-evolution/0305-swiftpm-binary-target-improvements.md at main · apple/swift-evolution · GitHub

Its a bit inconvenient when iterating, since in my case i had to split a single repository into two but overall its not too bad.

I now have one repo containing all the command-line tools (providing the artifactbundle in a github release) https://github.com/anreitersimon/swift-dependency-injection-cli/releases/tag/1.0.0

And another repo containing the "runtime" + the plugin (the plugin uses a binaryTarget to pull in the command-line tools swift-dependency-injection/Package.swift at 99073df2aca849ab1b21d1a242ff8e25a3f5aee6 · anreitersimon/swift-dependency-injection · GitHub

This has the nice side effect that users of the plugin dont have to build the the command-line tools and that saves some time.

Hope this helps

This seems like it’s fixed in Xcode 14 now, thanks to whoever for resolving this!

  • Fixed: Dependencies of package plugins always build for the host, regardless of what platform the client of the package the plugin is being applied to is building for. (91438186)
2 Likes

Xcode 14(b4) improves this situation: the tool is now built for the host, and runs correctly. However, Xcode still tries to build each tool for the target (eg. iPhone simulator) as well, which doesn't work if the tool has Mac-specific code.