SwiftPM: Trouble accessing non-Swift buildTool artifacts in derived data path

I'm trying to use Swift Package Manager's buildTool setup to generate static libraries that the Swift compiler can link to.

Right now, I’m using a prebuildCommand to run a shell script that compiles my Rust code as a static library for iOS, as I want any changes I make in Rust to be automatically reflected in my iOS build. The outputted Swift files in the prebuildCommand are accessible without any issue, however since the plugin has to run in a sandbox, any generated static libraries/XCFrameworks that get generated are stuck in the plugin’s work directory which resides within DerivedData in a non-deterministic folder name, and not automatically registered for linking with the Swift compiler.

The only two ways I can think of to solve this are:

  • Figure out a way to be able to copy the XCFramework or archive files out of the DerivedData plugin work directory and into one of my project folders (e.g. Sources), however not sure how to do that with the plugin sandbox restrictions
  • Figure out how to get a path to the DerivedData plugin work folder in Package.swift so that I can reliably link to the XCFramework via a binaryTarget or pass that path to the linkerSettings

I’m still relatively new to Swift/SPM, is there anything that I’m not thinking of or ways to solve one of the issues above? Or, am I thinking about this the wrong way?

Unfortunately, we're not really covering your use case with build tool plugins, they are pretty much limited to generating Swift code or resource files today.

I don't really have any suggestions for a temporary workaround, either. Even if you had access to a deterministic path to the binaries you're producing, we'd likely fail very early in the process during a clean build since we expect binaries to be present at the start of the build.

fwiw, I've heard you can do a Bazel build with your Swift package consuming a c static library target of the Rust package.

Here's an example of c calling rust code: bazelbuild/rules_rust/.../examples/ffi/c_calling_rust

The Swift rules
bazelbuild/rules_swift/../doc/rules.md#swift_library are low-level; for application-level builds, use apple_rules.

However, using Bazel relegates Xcode to consuming derived project state. https://bazel.build/migrate/xcode

@NeoNacho I'm still very new to SPM (and relatively new to Swift in general) but from my limited understanding it would be amazing if we could register some of the buildTool artifacts as binaryTargets, sort of like how generating Swift files automatically makes it available as a source. I feel like that would enable better interoperability with Rust and other languages that can compile a static library and Swift bindings.

I'm currently trying to work around this by calling my shell script from my package's build pre-action via the Xcode scheme editor. It's unfortunate since I won't get the nice declarative syntax that Package.swift provides and assume I lose some compatibility with the Swift build tools if I don't use Xcode to compile, but it seems like it should allow me to re-run the Rust build process every time I trigger a new build of my Swift project.

@wes1 Thanks for the info about Bazel! I've heard good things about it but never looked into it, will give these links a look - this might be the forcing function for me to finally get acquainted with it.