forked from ppy/osu
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathosu.Android.props
More file actions
156 lines (147 loc) · 10.7 KB
/
Copy pathosu.Android.props
File metadata and controls
156 lines (147 loc) · 10.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
<Project>
<PropertyGroup>
<SupportedOSPlatformVersion>33.0</SupportedOSPlatformVersion>
<!-- arm64-v8a only. Modern Android devices are 99%+ arm64; 32-bit arm32 (armeabi-v7a)
adds ~200 MB of duplicate native libraries for negligible device coverage.
x86 was already removed — modern x86_64 emulators run arm64 via translation. -->
<RuntimeIdentifiers>android-arm64</RuntimeIdentifiers>
<AndroidPackageFormat>apk</AndroidPackageFormat>
<!-- CJK and Mideast encodings are needed for song metadata display.
Rare covers supplementary Unicode blocks. West is the default Latin set. -->
<MandroidI18n>CJK;Mideast;Rare;West;Other;</MandroidI18n>
<AndroidHttpClientHandlerType>Xamarin.Android.Net.AndroidMessageHandler</AndroidHttpClientHandlerType>
<!-- NullabilityInfoContextSupport is disabled by default for Android -->
<NullabilityInfoContextSupport>true</NullabilityInfoContextSupport>
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
<!-- Do NOT enable AndroidEnableMarshalMethods on .NET 10 with profiled AOT + trimming.
The build-time P/Invoke stub generator can produce stubs that crash in native code
when combined with partial AOT and trimmed assemblies — the crash bypasses managed
exception handling entirely, resulting in silent SIGSEGV/SIGABRT with no crash logs.
This is especially dangerous for apps with complex interop (UnmanagedCallersOnly,
unmanaged function pointers, reflection-based handle discovery).
The default runtime-generated trampolines are slower but proven reliable. -->
<AndroidEnableMarshalMethods>false</AndroidEnableMarshalMethods>
<NoWarn>$(NoWarn);NU1608;XA4301</NoWarn>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<!-- Compress managed assemblies inside the APK (LZ4). Android extracts them on first run
but the download/APK size is significantly smaller. -->
<AndroidEnableAssemblyCompression>true</AndroidEnableAssemblyCompression>
<!-- Don't ship PDB files in the APK — they add ~15-20 MB.
Stack traces still work via embedded metadata. -->
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
<!-- Profiled AOT: pre-compile the hot methods listed in the bundled .NET / Android
profiles. Methods outside the profile remain JIT-compiled at runtime. NO trimming —
every method in every assembly stays present, so a profile-miss falls back to JIT
instead of throwing MissingMethodException like the previous trim+AOT combo did. -->
<AndroidEnableProfiledAot>true</AndroidEnableProfiledAot>
<!-- Concurrent Server GC. Server GC uses per-core allocation contexts and parallel
collections to reduce STW pause time, which is the largest source of >1ms frame
spikes on the Update/Draw threads. Memory usage rises ~20-30% in exchange. -->
<ServerGarbageCollection>true</ServerGarbageCollection>
<ConcurrentGarbageCollection>true</ConcurrentGarbageCollection>
<!-- Defensive explicit-set: SDK linker walks only Android SDK types, not user assemblies.
Making it explicit pins the behaviour so a future SDK bump cannot silently flip us to
Full linking and break reflection-heavy code paths in osu.Game / Realm / etc. -->
<AndroidLinkMode>SdkOnly</AndroidLinkMode>
<!-- Keep IL alongside profiled-AOT native code so any method outside the bundled
AOT profile has a JIT fallback instead of MissingMethodException at first call. -->
<AndroidStripILAfterAOT>false</AndroidStripILAfterAOT>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ppy.osu.Framework.Android" Version="2026.527.3" />
<!-- `ppy.osu.Framework.NativeLibs` is a transitive dependency of `ppy.osu.Framework`
that ships desktop-only natives (Linux/macOS/Windows) under `runtimes/<rid>/native/`
— including a bare Linux `libbass.so`/`libbass_fx.so`/`libbassmix.so` for linux-arm64.
The default .NET RID fallback graph routes `android-arm64` through
`linux-bionic-arm64` → `linux-arm64`, so NuGet resolves those Linux `.so` files for
an Android publish and the .NET Android SDK packs them into the APK's
`lib/arm64-v8a/` — replacing the correct Android arm64 natives from
`ppy.osu.Framework.Android`'s AAR (`jni/arm64-v8a/libbass*.so`). The Linux ELFs
reference GLIBC_* versioned symbols and cannot be loaded by Android's bionic
linker, crashing the app at startup with `System.DllNotFoundException: bass`.
Cutting the package's assets off at the NuGet level is the only reliable fix:
previous attempts to scrub items via an `AfterTargets="ResolveRuntimePackAssets;
ComputeFilesToPublish;ComputeResolvedFilesToPublishList"` target (see
`FixRuntimePackAssetTypes` below) consistently missed one of the many item
collections the .NET Android SDK consults during APK packaging, and the Linux
`.so` leaked into the APK (observed in build v2026.421.144 and again in v145
after a more thorough item filter). The package has no managed assets we need
— all Android runtime natives come from the `ppy.osu.Framework.Android` AAR —
so excluding it entirely is safe. -->
<PackageReference Include="ppy.osu.Framework.NativeLibs" Version="2025.806.0-nativelibs"
ExcludeAssets="all" PrivateAssets="all" />
</ItemGroup>
<PropertyGroup>
<!-- Fody does not handle Android build well, and warns when unchanged.
Since Realm objects are not declared directly in Android projects, simply disable Fody. -->
<DisableFody>true</DisableFody>
</PropertyGroup>
<!-- Fix for .NET 10 Android AOT misclassifying native .so assets in runtime pack.
Only .so files need the AssetType override — the previous broader filter
(all non-DLL, non-PDB) incorrectly reclassified signing metadata and config
files, which corrupted the APK signature (INSTALL_PARSE_FAILED_NO_CERTIFICATES).
Scoped to Release only to avoid interfering with Debug builds.
IMPORTANT: only Android-runtime .so files may be marked as native and packaged
into the APK. ppy.osu.Framework transitively depends on ppy.osu.Framework.NativeLibs
which ships desktop-only natives under runtimes/linux-arm64/native/, runtimes/osx/native/,
runtimes/win-*/native/ etc. — including a bare libbass.so, libbass_fx.so, libbassmix.so
for Linux. If any of those are marked AssetType=native, the .NET Android SDK packs them
into lib/arm64-v8a/ of the APK, racing with (and replacing) the proper Android arm64
libbass*.so coming from ppy.osu.Framework.Android's AAR (jni/arm64-v8a/). The Linux ELF
is linked against glibc and cannot be loaded by Android's bionic dynamic linker, which
surfaces at startup as System.DllNotFoundException: bass from AudioManager..ctor →
ManagedBass.Bass.get_DeviceCount, immediately crashing the app.
Implementation notes:
* Uses a two-step item helper list + Remove-by-identity pattern, which batches
over item metadata reliably (an earlier inline `Remove="@(X)" Condition="…"`
with a nested OR of Replace/Contains calls silently no-op'd — the Linux libbass
leaked into the APK in build v2026.421.144).
* Uses a single inverted match: any `.so` under `/runtimes/<rid>/native/` whose
RID does not start with `android` is stripped. This covers every non-Android
desktop/mobile RID in one rule and is future-proof against new RIDs.
* The path match uses forward-slash normalisation with the doubled-backslash
escape `'\\'` required inside MSBuild property-function string literals
(the previous `'\'` form is ambiguous to the expression parser).
* Applied to `ResolvedFileToPublish` (the primary input to the .NET Android SDK's
publish step) and also to `ReferenceCopyLocalPaths` / `RuntimeCopyLocalItems`
in case a NuGet package flows native assets through those collections. -->
<Target Name="FixRuntimePackAssetTypes" AfterTargets="ResolveRuntimePackAssets;ComputeFilesToPublish;ComputeResolvedFilesToPublishList"
Condition="'$(Configuration)' == 'Release'">
<ItemGroup>
<!-- Build filter lists of non-Android runtime .so files. The two-step pattern
(Include into helper list, then Remove by identity) is more reliable than a
conditional Remove because MSBuild batches per-item on metadata references
in the helper list's Condition, and the subsequent Remove is an unconditional
identity match. -->
<_DesktopNativeToStripFromPublish Include="@(ResolvedFileToPublish)"
Condition="'%(Extension)' == '.so'
AND $([System.String]::Copy('%(Identity)').Replace('\\','/').Contains('/runtimes/'))
AND !$([System.String]::Copy('%(Identity)').Replace('\\','/').Contains('/runtimes/android'))" />
<ResolvedFileToPublish Remove="@(_DesktopNativeToStripFromPublish)" />
<_DesktopNativeToStripFromRefCopy Include="@(ReferenceCopyLocalPaths)"
Condition="'%(Extension)' == '.so'
AND $([System.String]::Copy('%(Identity)').Replace('\\','/').Contains('/runtimes/'))
AND !$([System.String]::Copy('%(Identity)').Replace('\\','/').Contains('/runtimes/android'))" />
<ReferenceCopyLocalPaths Remove="@(_DesktopNativeToStripFromRefCopy)" />
<_DesktopNativeToStripFromRuntimeCopy Include="@(RuntimeCopyLocalItems)"
Condition="'%(Extension)' == '.so'
AND $([System.String]::Copy('%(Identity)').Replace('\\','/').Contains('/runtimes/'))
AND !$([System.String]::Copy('%(Identity)').Replace('\\','/').Contains('/runtimes/android'))" />
<RuntimeCopyLocalItems Remove="@(_DesktopNativeToStripFromRuntimeCopy)" />
<!-- Only mark Android-RID runtime pack assets as native. RuntimePackAsset items
carry an explicit RuntimeIdentifier metadata, so this filter is exact. -->
<RuntimePackAsset Update="@(RuntimePackAsset)"
Condition="'%(Extension)' == '.so'
AND $([System.String]::Copy('%(RuntimeIdentifier)').StartsWith('android'))">
<AssetType>native</AssetType>
</RuntimePackAsset>
<!-- Mark the surviving (Android-only after the Remove above) publish .so files as native
so the Android SDK packs them into lib/<abi>/ rather than dropping them as data. -->
<ResolvedFileToPublish Update="@(ResolvedFileToPublish)" Condition="'%(Extension)' == '.so'">
<AssetType>native</AssetType>
</ResolvedFileToPublish>
</ItemGroup>
</Target>
</Project>