💡 Tips

macOSの.appが起動直後にクラッシュする — SwiftPMのrpath問題と直し方

App Store 外で配布する macOS アプリを SwiftPM でビルドし、.app/Contents/Frameworks/ に framework(Sparkle など)を同梱したら、ダブルクリックしても一瞬で落ちる——。これは SwiftPM が吐くバイナリの rpath が原因だ。結論を先に言うと、framework コピー後・署名前に install_name_tool で rpath を1行追加するだけで直る。実際に踏んで解決した手順を共有する。

何が起きているか

swift build が生成する実行バイナリの LC_RPATH(ライブラリ検索パス)には、@loader_path/usr/lib/swift などは入るが、@executable_path/../Frameworks は入らない

そのため .app/Contents/Frameworks/ に framework を置いても、dyld が @rpath/Sparkle.framework/... を解決できず、起動直後に必ずクラッシュする。

Library not loaded: @rpath/Sparkle.framework/Versions/B/Sparkle
  tried: '.../Contents/MacOS/Sparkle.framework/...' (no such file)

@loader_path(=MacOS ディレクトリ)しか見ていないので、一つ上の Frameworks に到達できないわけだ。

直し方: rpath を1行追加する

framework をコピーした後、コード署名する前に、実行バイナリへ rpath を追加する。

install_name_tool -add_rpath "@executable_path/../Frameworks" \
  "$APP/Contents/MacOS/$BIN"

これで dyld が .app/Contents/Frameworks/ を探しに行けるようになる。

⚠️ 必ず署名前に実行すること。 署名後に書き換えると署名が無効化され、Gatekeeper に弾かれる。

冪等にしたいなら、既に追加済みかを事前チェックするとよい。

if ! otool -l "$BIN" | grep -q "path @executable_path/../Frameworks"; then
  install_name_tool -add_rpath "@executable_path/../Frameworks" "$BIN"
fi

診断のコツ

「ダブルクリックしても起動しない」は情報が少なく原因を見失いがちだ。次の順で当たると早い。

  1. クラッシュログを読む: ~/Library/Logs/DiagnosticReports/<App>-*.ips termination.namespace = DYLDLibrary missing が出ていればこの問題。
  2. rpath の現状を確認: otool -l <bin> | grep -A2 LC_RPATH

@executable_path/../Frameworks が一覧に無ければ、上の手順で追加すればよい。

配布時のもう一つの罠: ._* ファイル混入

ついでに配布段階の沼も一つ。拡張属性付きのバンドルを zip -r で固めると、各ファイルが ._Foo(AppleDouble)として混入し、解凍後に署名の封印と不一致になって Gatekeeper に弾かれることがある。配布 zip は メタデータを除外して 作るのが安全だ。

ditto -c -k --keepParent --norsrc --noextattr "$App.app" "$App.zip"

厄介なのは、Finder の解凍では ._* が復元されず手元では再現しない点。検証は必ず ditto -x -k で展開し、find App.app -name '._*' | wc -l が 0 か確認する。

さらに学ぶなら

dyld・コード署名・公証まわりは、断片的な対処の積み重ねよりも仕組みを腰を据えて理解した方が結局は速い。手元に1冊リファレンスを置いておくと、こうした沼で消耗しなくなる。

📚 おすすめ書籍

macOSとiOSのプログラミング 詳解

dyldや署名の理解に

Amazonで見る →

まとめ

  • SwiftPM のバイナリには @executable_path/../Frameworks の rpath が付かない
  • framework 同梱時は 署名前install_name_tool -add_rpath で追加する
  • クラッシュは .ips ログの DYLD / Library missing で診断できる
  • 配布 zip は ditto --norsrc --noextattr._* 混入を防ぐ