Skip to content

Commit f02e34b

Browse files
authored
Fix Metal renderer crash in packaged apps (Bundle.module fatalErrors) (#593)
MetalTerminalRenderer.candidateBundles() included Bundle.module as its first candidate for locating SwiftTerm_SwiftTerm.bundle (the shaders). SwiftPM's generated accessor for executable targets doesn't behave like a normal Bundle lookup: it fatalErrors (aborting the whole process, not throwing or returning nil) if the bundle isn't found at either of its two hardcoded candidates - Bundle.main.bundleURL (the .app's root, not Contents/Resources where a packaged .app actually places SwiftPM resource bundles) or the build machine's absolute .build/.../ SwiftTerm_SwiftTerm.bundle path baked into the binary at compile time. The practical effect: any app that depends on this package via SPM, ships the resource bundle correctly in its own build pipeline, and enables the Metal renderer will crash instantly on any machine other than the one that built it - candidateBundles()'s other two fallbacks (Bundle(for:), Bundle.main) never get a chance to run, because merely evaluating the Bundle.module property is what crashes. Fix: probe for the same bundle by name ourselves via Bundle(url:), which is a normal failable initializer (verified it returns nil for a missing path and a working bundle for a present one, no fatalError either way) - mirroring the generated accessor's own main-bundle- relative lookup, plus the Contents/Resources location it misses. Falls through to the existing fallback candidates exactly as before for any environment where the old code didn't crash.
1 parent ac99a54 commit f02e34b

1 file changed

Lines changed: 24 additions & 1 deletion

File tree

Sources/SwiftTerm/Apple/Metal/MetalTerminalRenderer.swift

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2770,7 +2770,30 @@ final class MetalTerminalRenderer: NSObject, MTKViewDelegate {
27702770
private static func candidateBundles() -> [Bundle] {
27712771
var bundles: [Bundle] = []
27722772
#if SWIFT_PACKAGE
2773-
bundles.append(Bundle.module)
2773+
// Deliberately not `Bundle.module`: SwiftPM's generated accessor for
2774+
// executable targets calls `fatalError` (aborting the whole process,
2775+
// not throwing) instead of returning nil when it can't locate
2776+
// `SwiftTerm_SwiftTerm.bundle`. Its only two candidates are
2777+
// `Bundle.main.bundleURL` (the .app's *root*, not `Contents/Resources`
2778+
// where a packaged .app actually puts SwiftPM resource bundles) and
2779+
// the build machine's absolute `.build/.../SwiftTerm_SwiftTerm.bundle`
2780+
// path baked into the binary at compile time. That means any app that
2781+
// bundles this package and ships the resource bundle correctly still
2782+
// crashes the instant Metal rendering is requested on a machine other
2783+
// than the one that built it. Probe for the same bundle name
2784+
// ourselves — mirroring the accessor's own main-bundle-relative
2785+
// lookup, plus the `Contents/Resources` location it misses — so a
2786+
// missing bundle falls through to the next candidate instead of
2787+
// aborting the process.
2788+
let bundleName = "SwiftTerm_SwiftTerm.bundle"
2789+
if let url = Bundle.main.resourceURL?.appendingPathComponent(bundleName),
2790+
let resourceBundle = Bundle(url: url) {
2791+
bundles.append(resourceBundle)
2792+
}
2793+
if let url = Bundle.main.bundleURL.appendingPathComponent(bundleName) as URL?,
2794+
let resourceBundle = Bundle(url: url) {
2795+
bundles.append(resourceBundle)
2796+
}
27742797
#endif
27752798
bundles.append(Bundle(for: MetalTerminalRenderer.self))
27762799
bundles.append(Bundle.main)

0 commit comments

Comments
 (0)