Swift Package Manager doesn't propagate a binaryTarget's headers automatically
15. Swift Package Manager doesn't propagate a binaryTarget's headers automatically
What happened: once the Libmpv.xcframework build itself was finally
green (see entry 13), the very next CI stage — building MPVKit as a
Swift package against that framework — failed immediately:
In file included from .../MPVKit/Sources/CMPV/cmpv_shim.c:1:
.../MPVKit/Sources/CMPV/include/cmpv_shim.h:4:10: fatal error: 'mpv/client.h' file not found
4 | #include <mpv/client.h>
Root cause: Package.swift declares CMPV (a C target) with
dependencies: ["Libmpv"], where Libmpv is the .binaryTarget wrapping
Libmpv.xcframework. This looks like it should be enough — and it is,
for Swift files that import Libmpv (see MPVCore.swift and others,
which work fine) — but it is not enough for a C target that reaches
for the framework's headers via a plain #include. This is a
well-documented Swift Package Manager limitation, not a mistake specific
to this project: multiple independent bug reports
(swiftlang/swift-package-manager#7626, a Swift Forums thread titled
exactly "Binary Target infer header search path", and several others)
describe the identical symptom against completely unrelated packages —
SPM does not automatically add a binary target's Headers/ directory to
a dependent C/C++/Objective-C target's header search path, only to
Swift's module-based import resolution.
Fix: added explicit cSettings: [.headerSearchPath(...)] entries to
CMPV's target definition in Package.swift, pointing directly at the
XCFramework's own internal per-platform Headers/ folders. This
project's XCFramework (built by buildscripts/scripts/mpv-ios.sh)
produces exactly two platform-slice folders — a device slice
(ios-arm64) and a lipo-merged simulator fat-binary slice
(ios-arm64_x86_64-simulator), matching the plain-static-library
XCFramework layout documented in several third-party writeups on the
format. Both paths are listed unconditionally; whichever one doesn't
apply to the current build target is simply not found and ignored by the
compiler, so this doesn't need to vary per-platform in the manifest
itself.
Lesson: a working import Libmpv elsewhere in the same package
doesn't guarantee every target can see the underlying headers — Swift's
module-based import and a C target's raw #include resolve through
different mechanisms in SPM, and only one of them benefits automatically
from a binary target dependency. Worth checking specifically whether a
failing target is a C/Objective-C target reaching for headers directly,
versus a Swift target doing a module import, since the fix differs
completely between the two.