.NET MAUI/AndroidのデバッグシンボルをGoogle Playにアップロードする完全手順(mapping.txtの扱いとエラー対策まで解説)

Google Play Console に「ネイティブコードを含む App Bundle にはデバッグシンボルがありません」と出たまま、何を ZIP にしてどこへアップロードするのか分からない――。本記事は .NET MAUI/Android(net9.0-android 前提)で生成される app_shared_libraries や mapping.txt の正体を解きほぐし、正しい ZIP 構成と Play Console 上の操作、よくある落とし穴、CI 連携までを一気通貫で整理します。

目次

想定読者とゴール

本記事は .NET MAUI で AAB を配信しており、Play Console の警告を解消してクラッシュ/ANR のスタックトレースをシンボリケートしたい開発者を対象とします。最終的に以下を達成します。

  • Release ビルドでネイティブデバッグシンボル(未圧縮 .so)を生成できる
  • arm64-v8a と x86_64 を正しく ZIP 化できる(余計な親フォルダー無し)
  • Play Console の「デバッグシンボル」欄に ZIP をアップロードできる
  • mapping.txt(R8/ProGuard)の取り扱いを理解し、別枠でアップロードできる

先に結論:何をどこへアップロードするか

最初に結論だけ示します。詳細は後述します。

Play Console の項目アップロードするもの拡張子生成場所(既定)ポイント
デバッグシンボル(Native debug symbols)フォルダー arm64-v8a と x86_64 を並べて ZIP 化.zipobj\Release\net9.0-android\app_shared_libraries\.so.manifest は ZIP に入れなくてよい。ZIP のルート直下に ABI フォルダーが並ぶ構造にする。
難読化解除ファイル(Deobfuscation files)mapping.txt.txt(単体)bin\Release\net9.0-android\mapping.txtJava/Kotlin 側のスタックトレース用。ネイティブとは別枠でアップロードする。

用語の整理:ネイティブシンボルと mapping.txt は別モノ

  • ネイティブデバッグシンボル:C/C++ やランタイムが生成する 未圧縮の .so(ストリップ前のシンボルを含むライブラリ)。Play Console の「デバッグシンボル」に ZIP としてアップロードします。
  • mapping.txt:R8/ProGuard の難読化解除用マップ。Java/Kotlin のクラッシュを可読化します。「難読化解除ファイル」に そのままアップロードします(ZIP 不要)。

前提条件

  • .NET SDK 9 以降(ターゲットフレームワーク:net9.0-android)
  • Release ビルドで AAB(App Bundle)を作成
  • Play Console で該当バージョンコードのリリースが作成済み

手順 1:Release ビルドでネイティブシンボルを生成する

まずは生成を有効化します。.csproj または Directory.Build.props に以下を追加(または確認)してください。

<PropertyGroup Condition="'$(Configuration)'=='Release'">
  <AndroidGenerateNativeDebugSymbols>true</AndroidGenerateNativeDebugSymbols>
  <!-- 必要なら:ストリップを無効化(サイズ肥大に注意)
  <AndroidStripNativeDebugSymbols>false</AndroidStripNativeDebugSymbols>
  -->
</PropertyGroup>

この設定で Release ビルド時、各 ABI 向けにシンボル付きの .so が app_shared_libraries 配下へ複製されます。生成を確認するには、以下のいずれかでビルドします。

dotnet publish -c Release -f net9.0-android -p:AndroidPackageFormat=aab

ビルド後の代表的な出力ツリーは次のとおりです。

obj\
  Release\
    net9.0-android\
      app_shared_libraries\
        arm64-v8a\
          libmonodroid.so
          libxa-internal-api.so
          lib{YourApp}.so
          ...(その他 .so)
          .so.manifest
        x86_64\
          libmonodroid.so
          lib{YourApp}.so
          ...(その他 .so)
          .so.manifest
bin\
  Release\
    net9.0-android\
      {YourApp}.aab
      mapping.txt

補足:プロジェクトの ABI 設定によっては armeabi-v7a 等が現れる場合があります。AAB に含めた ABI は すべて ZIP に同梱してください(片方だけだと Play 側で「ABI が足りない」扱いになります)。

手順 2:アップロード用 ZIP を正しく作る

Play Console が期待する ZIP 構成は「ZIP のルート直下に ABI フォルダー(例:arm64-v8a、x86_64)が並び、その下に .so が置かれている」形です。余計な親ディレクトリを作ると弾かれます。

OK な例(推奨)

native-symbols-v42.zip
├─ arm64-v8a/
│   ├─ libmonodroid.so
│   ├─ lib{YourApp}.so
│   └─ ...
└─ x86_64/
    ├─ libmonodroid.so
    ├─ lib{YourApp}.so
    └─ ...

NG 例(Play でエラーになりやすい)

ZIP 内の構成問題点
native-symbols-v42.zip > app_shared_libraries > arm64-v8a ...余計な親フォルダー(app_shared_libraries)が含まれている
native-symbols-v42.zip > arm64-v8a > *.so(x86_64 等が無い)AAB に含めた全 ABI を同梱していない
native-symbols-v42.zip に .so.manifest だけ入っている必要なのは .so 本体。.manifest は Play で不要

Windows(PowerShell)での作り方

プロジェクトルートから以下を実行します。obj\Release\net9.0-android\app_shared_libraries の直下にある フォルダーを指定するのがコツです。

$base = "obj\Release\net9.0-android\app_shared_libraries"
Compress-Archive -Path "$base\arm64-v8a","$base\x86_64" -DestinationPath "native-symbols-v42.zip" -Force

同梱する ABI が増えた場合は配列に追加してください(例:"$base\armeabi-v7a")。

ZIP の中身を確認する

誤った親フォルダーが含まれていないか、作成後に中身を必ず確認しましょう。Windows でも 7-Zip 等で階層を見れば一発ですが、コマンドで確認するなら OS に応じて次のようにチェックできます。

:: PowerShell(ZIP のルートに arm64-v8a/ x86_64/ が見えること)
[IO.Compression.ZipFile]::OpenRead("native-symbols-v42.zip").Entries |
  ForEach-Object FullName | Select-Object -First 5

手順 3:Play Console にアップロードする

  1. Play Console で該当アプリを開き、アプリ > リリース から対象トラック(内部テスト/アルファ/製品版 など)を選び、対象のリリース編集画面へ進みます。
  2. 画面下部にある デバッグシンボル セクションで、作成した native-symbols-*.zip をアップロードします。
  3. 同ページの 難読化解除ファイル(Deobfuscation files)セクションにて、bin\Release\net9.0-android\mapping.txt をそのままアップロードします(ZIP 化不要)。
  4. 変更を保存してリリースを更新します。処理が完了すると、対象バージョンコードのクラッシュ/ANR が順次シンボリケートされます。

重要:シンボルは バージョンコード ごとにひも付けられます。AAB を差し替える場合は versionCode をインクリメントするのが安全です(同一コードの差し替えは紐付け不整合の原因)。

.manifest は ZIP に入れるべき?

結論:入れなくて構いません。.so.manifest はビルド成果物のメタ情報で、Play Console の「デバッグシンボル」要件は「ABI フォルダー配下に未圧縮の .so があること」です。.manifest は ZIP から除外しても処理に影響しません(プロジェクトから削除する必要もありません)。

よくあるつまずきと対処法

症状/エラーメッセージ原因対処
「シンボルファイルの形式が無効」ZIP のルートに余計な親フォルダーがいる / .so が直下に置かれているZIP のトップに arm64-v8a、x86_64 など ABI フォルダーが並ぶ形に修正
「対象 ABI が足りない」AAB に含めた ABI と ZIP の ABI が一致していないプロジェクトの ABI 設定を確認し、ZIP に同じ ABI 群を入れる
クラッシュがシンボリケートされないバージョンコード不一致 / シンボル処理未完了対象クラッシュの versionCode を確認。必要なら再アップロード。以後は常に versionCode をインクリメント
Release なのに app_shared_libraries が空<AndroidGenerateNativeDebugSymbols> が false / Debug でビルドしているRelease 構成に切り替え、該当プロパティを true に設定
mapping.txt が見つからないR8/ProGuard 無効 / 生成パスを見誤っているbin\Release\net9.0-android を確認。なければ R8 を有効化(通常は既定で有効)
「同じファイルが既にアップロードされています」同じ ZIP を再送不要。差し替え時は ZIP を作り直す(内容が違えば自動で置き換えられる)

生成・アップロードの一連作業を再現性高く回す

手作業での取り違えを防ぐため、次のような小さなスクリプトで ZIP 作成を自動化すると堅牢です。

PowerShell スニペット(プロジェクト直下で実行)

# 1. Release Publish(AAB と mapping.txt を生成)
dotnet publish -c Release -f net9.0-android -p:AndroidPackageFormat=aab

# 2. ネイティブシンボル ZIP を作成

$base = "obj\Release\net9.0-android\app_shared_libraries"
$abis = @("arm64-v8a","x86_64") # 追加 ABI があれば増やす
$paths = $abis | ForEach-Object { Join-Path $base $_ }
Compress-Archive -Path $paths -DestinationPath "native-symbols-$((Get-Date).ToString('yyyyMMdd-HHmm')).zip" -Force

# 3. 出力の所在を表示

Write-Host "AAB: bin\Release\net9.0-android*.aab"
Write-Host "Symbols ZIP: native-symbols-*.zip"
Write-Host "mapping.txt: bin\Release\net9.0-android\mapping.txt" 

GitHub Actions の例(成果物を固めて配布用に)

name: build-android
on: [workflow_dispatch]
jobs:
  build:
    runs-on: windows-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-dotnet@v4
        with:
          dotnet-version: '9.0.x'
      - name: Publish AAB
        run: dotnet publish -c Release -f net9.0-android -p:AndroidPackageFormat=aab
      - name: Zip Native Symbols
        shell: pwsh
        run: |
          $base = "obj\Release\net9.0-android\app_shared_libraries"
          $abis = @("arm64-v8a","x86_64")
          $paths = $abis | ForEach-Object { Join-Path $base $_ }
          Compress-Archive -Path $paths -DestinationPath "native-symbols.zip" -Force
      - name: Upload Artifacts
        uses: actions/upload-artifact@v4
        with:
          name: android-release
          path: |
            bin\Release\net9.0-android\*.aab
            bin\Release\net9.0-android\mapping.txt
            native-symbols.zip

このように AAB、mapping.txt、native-symbols.zip をセットで保管しておくと、後から Play Console にアップロードし直す時も迷いません。

なぜ app_shared_libraries を ZIP にするのか

.NET MAUI の Android パイプラインは、パッケージング中にストリップ前(またはストリップ直後)の .so を app_shared_libraries に複製します。Play Console は「ネイティブスタックのシンボリケーション」に未圧縮の .so を必要とするため、この複製がそのまま「デバッグシンボル」として機能します。.so.manifest は各 .so の列挙リストですが、Play 側は ZIP を展開して ABI 配下の .so を直接読み取るため、.manifest 自体は不要です。

プロジェクト設定のベストプラクティス

  • Release のみに限定して生成:Debug では不要。条件付き PropertyGroup を用いる(本記事のサンプル参照)。
  • バージョンコードを毎回インクリメント:シンボルとの紐付けが崩れる事故を防止。
  • ABI を意識:arm64-v8a が主流。エミュレーター検証用に x86_64 を含めるなら、ZIP も両方同梱。
  • サイズとプライバシー:未圧縮 .so は大きい場合があります。リポジトリには含めず、アーティファクト管理で保管。

チェックリスト(公開前に確認)

  • <AndroidGenerateNativeDebugSymbols>true</...> が Release で有効になっている
  • Publish 後、obj\Release\net9.0-android\app_shared_libraries に ABI ごとの .so が見える
  • ZIP のトップレベルに arm64-v8a/ と x86_64/(必要に応じて追加 ABI)だけが並ぶ
  • .so.manifest は ZIP に含めない(含めても通る場合はあるが要件外)
  • bin\Release\net9.0-android\mapping.txt を別枠「難読化解除ファイル」でアップロード
  • Play Console の対象リリースがアップロードした AAB の versionCode と一致している
  • クラッシュが発生したら該当バージョンのレポートでシンボリケートされているか確認

発展トピック:C/C++ の独自ネイティブコードを含む場合

サードパーティや独自の NDK ライブラリ(libxyz.so)をバインド/同梱している場合でも、AndroidGenerateNativeDebugSymbols により app_shared_libraries 配下に未圧縮の .so が現れます。基本的にそのまま ZIP に含めれば OK です。もし別形式のシンボル(例:Breakpad/NDK の .sym)を運用しているプロジェクトでも、Play Console は「未圧縮 .so」でのアップロードを最も素直に受け付けます。迷ったら 実際に AAB に含まれている ABI と 同じ ABI の .so を ZIP に集めることを優先しましょう。

トラブルシュート:ケース別の深掘り

ケースA:app_shared_libraries がない/空

Release でビルドしているか、ターゲットフレームワークが net9.0-android か、プロパティ名のタイプミスがないかを確認してください。CI では構成が Debug のままになっていることがよくあります。

ケースB:ABI を減らしたらクラッシュが読めなくなった

過去のバージョンに対しては、当時の ABI 群に対応したシンボルが必要です。特定 ABI を除外した新バージョンで検証しても、旧バージョンのクラッシュには影響しません。対象クラッシュの versionCode と ABI を必ず確認します。

ケースC:versionCode を変えずに AAB を差し替えた

Play 側のシンボル・クラッシュ紐付けは versionCode に依存します。差し替えでバイナリが変わると、スタックの解決に失敗することがあります。差し替えは避け、ビルド更新時は versionCode をインクリメントしましょう。

ケースD:リリース画面のどこに「デバッグシンボル」があるか分からない

Play Console の UI はときどき名称や配置が変わりますが、原則として「リリースを作成/編集」画面の下部に付属セクションが並びます。項目名は「デバッグシンボル」「Native debug symbols」「難読化解除ファイル(Deobfuscation files)」などです。見つからない場合は対象のリリース編集画面を開いて下までスクロールしてください。

実運用のコツ

  • クラッシュ再現用の小さなスイッチ:内部テストビルドに限定した「強制クラッシュ」ボタンを用意すると、シンボル適用の検証が容易です。
  • アーティファクトをバージョン単位で束ねる:{versionCode}/ のディレクトリに .aab、mapping.txt、native-symbols.zip を保管しておくと調査が速い。
  • ZIP 名にバージョンを刻む:例 native-symbols-v123.zip。Play 上でも識別しやすくなります。

FAQ

  • Q:.so.manifest は ZIP に入れるべき?
    A:不要です。.so 群だけで充分です(入っていても通る場合はありますが、要件ではありません)。
  • Q:mapping.txt は ZIP にまとめる?
    A:いいえ。単体ファイルを「難読化解除ファイル」にアップロードします。
  • Q:どのタイミングでアップロードすべき?
    A:対象 AAB と同じ versionCode のリリース作成時(または直後)が最適。遅れても同一コードなら後追いでシンボリケーションされます。
  • Q:ABI が arm64-v8a のみでも良い?
    A:アプリのサポート方針次第です。AAB に含めた ABI と ZIP の ABI が一致していれば問題ありません。

まとめ

Play Console の「ネイティブコードを含む App Bundle にはデバッグシンボルがありません」という警告は、.NET MAUI/Android でも手順さえ把握すれば確実に解消できます。ポイントは ① Release で AndroidGenerateNativeDebugSymbols を有効化して未圧縮 .so を生成、② app_shared_libraries 直下の ABI フォルダーをそのまま ZIP 化(余計な親階層は作らない)、③ mapping.txt は別枠でアップロード、の三点です。以降、バージョンコードを正しく運用し、AAB・mapping・シンボル ZIP をワンセットで管理しておけば、クラッシュ解析の生産性は大きく向上します。

付録:すぐ使えるテンプレ集

プロジェクト設定テンプレ(Release のみ)

&lt;PropertyGroup Condition="'$(Configuration)'=='Release'"&gt;
  &lt;TargetFramework&gt;net9.0-android&lt;/TargetFramework&gt;
  &lt;AndroidPackageFormat&gt;aab&lt;/AndroidPackageFormat&gt;
  &lt;AndroidGenerateNativeDebugSymbols&gt;true&lt;/AndroidGenerateNativeDebugSymbols&gt;
  &lt;!-- 必要に応じて:AOT/PGO 等は別設定 --&gt;
&lt;/PropertyGroup&gt;

ZIP に入れる/入れない早見表

対象入れる?理由
arm64-v8a\*.so入れるネイティブシンボル本体
x86_64\*.so入れる同上
*.so.manifest入れない(任意)Play で要求されていないメタ情報
mapping.txt入れない難読化解除ファイルは別セクションに単体でアップロード
{YourApp}.aab入れないリリース本体はシンボル ZIP と別管理

チェックコマンド断片(目視確認用)

# PowerShell: ZIP 直下の最初の 10 件を表示
[IO.Compression.ZipFile]::OpenRead("native-symbols.zip").Entries |
  Select-Object -First 10 | ForEach-Object FullName

以上の手順で、Play Console の警告は解消され、クラッシュ/ANR レポートは可読なスタックトレースへと復元されます。日々の配信フローに組み込み、安定した障害解析基盤を構築しましょう。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次