desktop-and-build-footguns について
デスクトップJVMおよびCompose Multiplatformアプリの開発者向けスキルです。ネイティブバインディング、メディア、パッケージング、ビルドの落とし穴を症状別に整理し、対処パターンと検証コマンドを示します。
主な機能
- 01トピック別の短い参考ファイル24件
- 02JVMネイティブバインディングとメディアの指針
- 03パッケージング、署名、ビルドの注意点
- 04不具合の症状と実装パターン
- 05変更を検証するコマンド
活用シーン
- 01クリーン環境でのネイティブライブラリ障害の調査
- 02デスクトップJVMアプリへのメディアエンジン組み込み
- 03macOSアプリのパッケージングと署名の確認
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ maxrave-dev/ kotlin-footguns --skill desktop-and-build-footgunsインストール後、Claude Code で/desktop-and-build-footgunsを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/arm64-native-gap-audit.mdbuildkonfig-secrets-flavors.mdbundled-native-soname-conflict.mdci-flaky-timing-luck.mdcompose-desktop-video-no-swingpanel.mdconfig-fails-open-verify-artifact.mdconveyor-desktop-packaging.mddesktop-deep-link-plumbing.mddesktop-system-media-integration.mdembed-media-engine-desktop.mdgithub-actions-multiplatform-release.mdgradle-config-resolved-too-early.mdjna-native-binding-traps.mdjvm-desktop-memory-footprint.mdmacos-codesign-sidecar-strip.mdmacos-lsenvironment-path-pin.mdmerge-split-av-streams-desktop.mdnative-artifact-for-embedding.mdr8-proguard-desktop-survival.mdreproducible-native-bundling-two-tasks.mdtransitive-version-pinning.mdupstream-lib-bug-workaround-template.mdwindows-msix-offline-installer.mdwindows-vm-detection-post-wmic.md詳細
デスクトップJVMとビルドの落とし穴
本番環境のKotlinおよびCompose Multiplatformアプリから掘り起こした24の落とし穴です。それぞれreferences/配下の短いファイル1つにまとめられています。各ファイルには、実際に動作するパターン、実際に起こる具体的な失敗例とその理由を示す落とし穴、およびユーザー自身のツリーで実行するコマンドを記載した検証方法のセクションがあります。
このインデックスの使い方。 これから書こうとしているコード、または調べている症状に照らして、以下の項目を確認してください。各項目には、トピックと、その項目を参照すべき症状が記載されています。該当する可能性のあるファイルは、コードや修正を提案する前に、必ずすべてReadツールで読んでください。各ファイルは150行未満です。検証コマンドにソースプロジェクトのパスが指定されている場合は、ユーザーのツリー内の対応するパスに置き換えてください。
相互参照。 ファイル内にバッククォートで囲まれた落とし穴の名前があり、それがここに記載されていない場合は、隣接する領域のスキルにあります。../*/references/<name>.mdをglobして開いてください。
デスクトップJVM上のネイティブコード
jna-native-binding-traps— JNA(Java Native Access)を使って C ライブラリの JVM バインディングを手書きする際の落とし穴です。Windows では別の意味になるオープンフラグオプション、オフセットを直接指定して読み取る構造体、バインディングが弱参照で保持するコールバック、登録が遅すぎる検索パス、実際にどのファイルが開かれたかの確認方法を扱います。すべての開発者のマシンでは動くのにクリーンな環境では失敗する場合や、ライブラリがすぐそこにあるのに、最初のシンボル検索で「指定されたモジュールが見つかりません」というエラーが出る場合にご活用ください。native-artifact-for-embedding— JVM アプリが実際に読み込んで配布できるネイティブ共有ライブラリの作成方法です。ビルド済みのポータブルバンドルが通常は読み込み可能なライブラリではない理由、サポート対象とする最も古いベースシステムでのビルド、ランパスの選択、バンドルから意図的に除外すべきライブラリ、読み込みと初期化のスモークテストをビルドの必須条件にする方法を扱います。バンドルしたネイティブライブラリがすべての開発者のマシンでは動くのにクリーンな環境では失敗する場合や、アプリがひそかに自分のものではなくシステム全体にインストールされたコピーを使っていたと判明した場合にご活用ください。bundled-native-soname-conflict— デスクトップアプリにバンドルしたネイティブライブラリが汎用ベースライブラリのコピーも同梱し、そのコピーがネイティブライブラリの読み込み直後にプロセス全体の共有オブジェクト名を占有した結果、無関係なプラットフォーム API が別のライブラリ名を含むシンボル不足エラーを返す問題を扱います。リンクやシステムダイアログを開く機能が自分のマシンでは動くのにユーザーのマシンでは何も起きない場合、プラットフォーム API が実行時に未サポートと報告する場合、ネイティブバンドルに含めるものを決める場合、またはまさにこのバグに対する修正をマージしたのにユーザーの環境で変化が見られない場合にご活用ください。macos-codesign-sidecar-strip— macOS が拡張属性を持つファイルの隣に作成する._*の付属ファイルを、macOS アプリバンドルに何かを展開した後、バンドルへの署名前に削除します。署名時にこれらの付属ファイルも通常のバンドルメンバーとして封印され、OS がファイルマネージャーでアプリを操作した最初の時点で削除するためです。署名済みの macOS アプリがビルドマシンでは問題なく起動するのに、ユーザーには破損していて開けないと表示される場合や、署名検証で封印されたリソースが見つからないと報告される場合にご活用ください。jvm-desktop-memory-footprint— デスクトップ JVM アプリケーションのメモリ使用量を正しく評価し、削減する方法です。常駐メモリの数値ではなくヒープと全体のメモリ使用量の比率を確認し、リークとアロケーターが解放済みページを保持している状態を切り分ける実験を行い、スレッドごとのアロケーターアリーナがユーザーのコア数に応じてメモリ使用量を変える理由を理解します。デスクトップアプリが約 1 GB の使用量で落ち着く、または長時間のセッションで増え続ける場合、大規模なマシンを使うユーザーのメモリ使用量が再現できないほど多い場合、あるいはアロケーターやガベージコレクターのフラグを調整する前にご活用ください。embed-media-engine-desktop— ネイティブの C メディアエンジンを JVM デスクトップアプリに埋め込む方法です。メディア項目ごとにハンドルを 1 つ使うこと、専用のイベントポンプスレッド、出力ドライバーを固定して正しい順序でレンダーコンテキストを作成すること、ハンドルを解放するスレッドにすべてのプロパティ書き込みを限定すること、オプションのエンジン設定を機能検出すること、エンジンが解析できる形式で数値を整形することを扱います。プロパティ設定中にアプリが停止する場合、エンジンが独自のウィンドウを開く場合、またはアプリが明らかに設定した値が無視される場合にご活用ください。merge-split-av-streams-desktop— メディアエンジンの編集リスト URL 形式を使い、別々の音声のみのストリームと動画のみのストリームを JVM デスクトップで 1 つのソースとして再生する方法です。メディアソースをマージするデスクトップ版として、ストリーム URL を長さ付きで引用符に入れる方法や、クロスフェードの途中で URL 2 つをマージした項目を準備してはいけない理由も扱います。デスクトップで動画を再生するとログに何も出ないまま完全に無音になる場合、ストリーム URL が最初のセミコロンで切れてしまう場合、または動画トラックへのフェード時に再生中の曲が途中で切れる場合にご活用ください。compose-desktop-video-no-swingpanel— 重量級の AWT パネルを埋め込まずに、ネイティブメディアエンジンの動画フレームを Compose Desktop で描画する方法です。完成したフレームを StateFlow で不変のスナップショットとして公開して通常の Image で描画すること、UI スレッド外で変換すること、エンジンのピクセルバイト順に合わせること、フィット方法をエンジンに任せることを扱います。埋め込んだ動画が Z 順序にかかわらずすべての要素の上に表示される場合、スクロール中に 1 フレーム遅れる場合、2 台目の画面にプレーヤーを表示した途端に一方の画面が黒くなる場合、赤と青が入れ替わって表示される場合、または content-scale では除去できない黒帯が表示される場合にご活用ください。desktop-system-media-integration— ファサードの背後で各 OS のシステムの再生中情報と再生操作インターフェースにデスクトップアプリを接続し、ネイティブ初期化に失敗しても再生が停止しないよう、その連携だけを無効にします。パッケージ化したアプリ内でプラットフォームのメインスレッド上に初期化する方法、JVM のネイティブアクセスレイヤーを介して OS のメディアフレームワークを利用する方法、OS に渡すコールバックへの強参照の保持、連携を専用スレッドに限定する方法を扱います。メディアキーやシステムの再生中パネルが通常の Gradle 実行では動くのにパッケージ化したアプリでは動かない場合、トラック名ではなくアプリ名だけが表示される場合、またはしばらくすると再生操作のコールバックが届かなくなる場合にご活用ください。upstream-lib-bug-workaround-template— 修正できないライブラリのバグに対する回避策の構成方法です。実際に追跡できた範囲まで仕組みをたどり、受け入れたトレードオフを明示し、失敗しても無言で終わらず機能が低下するように逃げ道を残し、削除するための明確な条件を記し、ライブラリの他のユーザーには起きないバグが自分の使用パターンで顕在化する理由を記録します。他者のバグを理由に設定を固定する、コードパスを避ける、または防御的な分岐を追加しようとしている場合や、削除できる条件がコメントに書かれていない回避策をレビューする場合にご活用ください。macos-lsenvironment-path-pin— パッケージ化した macOS アプリのプロパティリストで Launch Services の環境辞書(LSEnvironment)を宣言すると、プロセスのPATHがシステムの 4 つの標準ディレクトリに固定されるため、アプリが起動するすべての外部プロセスから、それ以外の場所にインストールされたものが利用できなくなります。macOS アプリバンドルに環境変数を追加する前、アプリからシェル経由で実行するヘルパーがインストール済みのユーザーに限って「コマンドが見つかりません」と報告する場合、またはターミナルからの起動では動く機能が Dock から起動すると何も起きない場合にご活用ください。
パッケージ化、署名、CI、ビルド
conveyor-desktop-packaging— HOCON 設定が未知のキーを黙って無視する、設定駆動型パッケージャーを使って JVM デスクトップアプリをパッケージ化します。アプリレベル、OS ごとのセクション、ネストされたグループのどこにキーを設定するか、コマンドラインからのキーの上書き、パッケージング用 JDK の固定、そして PATH をひそかに固定する環境ブロックについて説明します。記述したキーがビルド済みインストーラーに反映されず、ビルドログにも何も表示されない場合に参照してください。config-fails-open-verify-artifact— 未知のキーを無視してフェイルオープンする形式で、設定駆動型の機能を設定ではなく生成されたアーティファクトで検証します。2 回のビルドによる A/B 差分、grep するアーティファクトのフィンガープリント、そして問題の再発を防ぐ CI アサーションについて説明します。設定キーは正しく見え、ビルドも成功しているのに、設定した機能が実際に動作するところを一度も確認できていない場合に参照してください。desktop-deep-link-plumbing— JVM デスクトップアプリでカスタム URL スキームを最初から最後まで接続します。OS ごとの登録、起動時の引数フィルター、シングルインスタンスへの転送、コールバックのトークンをアプリの状態に渡す方法について説明します。リンクをクリックしたり、ブラウザーからリダイレクトで戻ったりしても、アプリが前面に出るだけで、本来完了するはずの処理が止まったままの場合に参照してください。r8-proguard-desktop-survival— JVM デスクトップアプリにバイトコードシュリンカーを適用する際、無効にしておくべき最適化の種類とその理由、難読化によって描画層とリフレクション層が壊れる理由、ネイティブバインディング、コルーチン、HTTP クライアントを使うアプリに必要な keep ルールの種類、縮小後の jar をパッケージャーに渡す方法について説明します。リリースビルドで検証エラーが発生する、透けたような画面や真っ白な画面が描画される、または開発時の実行では問題ないのにパッケージ済みインストーラーでのみ失敗する場合に参照してください。gradle-config-resolved-too-early— 「子設定の解決後に設定を変更できない」という形式の Gradle エラーを診断します。メッセージが解決したプラグインではなく、変更しようとした設定の名前を示す理由、最小限の動作するテンプレートを使ってプラグインを二分探索する方法、そして効果がないと記録されている修正案について説明します。ごく普通の依存関係の行を追加するとビルド設定が失敗し、その行をあらゆる方法で書き換えても何も変わらない場合に参照してください。transitive-version-pinning— 厳密なバージョン制約を持つ推移的依存関係が指定したバージョンを上書きし、ビルド全体で共有される下位ライブラリのバージョンを引き上げたり引き下げたりする場合の対処方法を説明します。どの依存関係が何を固定したかを特定する方法、バージョンを強制的に戻すべきときと固定されたバージョンにすべてをそろえるべきときの判断方法、そして誰かがアップグレードして再び問題を起こさないように固定理由を文書化する方法を扱います。無関係なライブラリを 1 つ追加しただけで、触れていないコンポーネントの実行時、描画処理中にメソッドが見つからないエラーが発生する場合に参照してください。reproducible-native-bundling-two-tasks— 異なる場所で動作する 2 つの Gradle タスクにバンドル処理を分け、ビルド済みネイティブライブラリをマルチプラットフォームのデスクトップビルドに含めます。開発マシンのタスクは各スライスをビルドして tarball にまとめ、ダイジェストを出力します。CI タスクはダウンロードのみを行い、ビルドファイルに固定されたダイジェストと照合して検証します。CI に不要なネイティブツールチェーンが必要な場合、ネイティブライブラリを更新したのに以前のバイナリがひそかに配布されてしまう場合、またはパッケージ済みインストーラーの起動時にネイティブライブラリが丸ごと欠落している場合に参照してください。windows-vm-detection-post-wmic— Windows 11 で従来のコマンドライン管理クエリツールが削除された後も、Windows 上のデスクトップアプリが仮想マシン内で実行されていることを検出します。PowerShell 経由で管理レイヤーにクエリを送り、製造元フィールドとモデルフィールドの両方を調べ、失敗時の動作を意図的に選ぶ方法を説明します。プロセスは動作し続けているのに、仮想マシン上で透過または装飾なしのウィンドウに何も描画されない場合や、何年も動いていた検出処理が突然すべての最新ホストで空の値を返す場合に参照してください。windows-msix-offline-installer— セルフ署名 MSIX(最新の Windows アプリケーションパッケージ形式)を、ホストされた更新サイトなしでエンドユーザーが実際にインストールできるように配布します。パッケージの横にインストールスクリプトと署名証明書を同梱し、信頼設定からサイドロードまでの手順をスクリプト化して、リリース間で署名キーを安定して維持します。パッケージャーの出力をダブルクリックしても失敗する場合、ラッパーインストーラーが 404 を返す URL の取得時に異常終了する場合、または新しいビルドを前のバージョンに上書きインストールできない場合に参照してください。arm64-native-gap-audit— マルチプラットフォームのデスクトップビルドで対象アーキテクチャをサポートすると約束する前に、その CPU アーキテクチャ向けのすべてのネイティブ依存関係を監査します。ネイティブライブラリが 1 つでも欠けていると、ビルド時ではなく初回使用時に対象全体が動かなくなるため、解決済みアーティファクトを繰り返し検査できるコマンドを用意し、依存関係を更新するたびに再実行してください。ARM64 対象を追加するか判断する場合や、ビルドとインストールは問題なく完了したのに、データベース、描画層、メディア層に初めてアクセスした時点で停止する場合に参照してください。ci-flaky-timing-luck— タイミングが偶然合ったときにしか成功しない CI ステップを見つけて修正します。同じ名前のマウント済みボリュームを非同期で切り離す処理が、次の反復処理のマウントと競合する問題や、ダウンローダーがエラーページをアーティファクトとしてひそかに保存する問題について説明します。何か月も成功していたステップがランナーイメージの更新後に失敗し始めた場合や、ジョブ自体は成功するものの、数分後に渡されたファイルが破損または空であるため別の処理が失敗する場合に参照してください。github-actions-multiplatform-release— マルチプラットフォームのデスクトップアプリ向け GitHub Actions リリースパイプラインを構成します。1 台の Linux ランナーで全プラットフォームのアーティファクトをクロスビルドし、2 つ目の小規模な macOS ジョブでは、本当に macOS が必要な唯一の手順だけを実行します。両者の間でアーティファクトを受け渡し、コストの高いランナーではコンパイルしません。リリースワークフローで 3 つの OS ジョブがそれぞれすべてを再ビルドしている場合や、パイプラインが正しく署名したアプリなのに macOS ユーザーに強制ブロックのダイアログが表示される場合に参照してください。buildkonfig-secrets-flavors— BuildKonfig を使って Kotlin Multiplatform アプリにビルド時設定を組み込みます。追跡対象外のローカルプロパティファイルからシークレットを読み込み、生成された定数として注入します。シークレットがない場合は空文字列を使用し、ビルドを失敗させるのではなく機能を無効にします。さらに、新しい Gradle で必要となる、生成ソースタスクとその利用側とのタスク依存関係の設定についても説明します。共通コードでコンパイル時定数が必要な場合、オープンソースビルドに認証情報を含めてはならない場合、または生成ソースタスクと利用側の間に暗黙の依存関係があるためビルドが失敗する場合に参照してください。
コメント
Markdown 対応