Commit 92676ea
committed
chore: build & run SamplesApp.Skia.netcoremobile with NativeAOT on Android
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463
Context: AwesomeAssertions/AwesomeAssertions#290
#21256 added support for building with .NET 10, and
one of the new features in .NET 10 is that Android has (very!)
preliminary preview support for [NativeAOT][0].
As building `SamplesApp.Skia.netcoremobile.csproj` with NativeAOT
takes a significant amount of time and disk space, we've decided to
introduce a new `Tests - Android+NativeAOT Skia` stage to build and
run these unit tests within an Android+NativeAOT environment.
To help reduce disk usage, after building the `.apk` we delete the
`obj` directory.
Update `android-run-skia-runtime-tests.sh` to always create
the `$(build.sourcesdirectory)/build/uitests-failure-results` path
before existing with an error, as failure to do so means that the
`PublishBuildArtifacts@1` YAML task fails:
##[error]Publishing build artifacts failed with an error: Not found PathtoPublish: /agent/_work/1/s/build/uitests-failure-results
which in turn means subsequent `DownloadBuildArtifacts@0` /
**Download previous test runs failed tests** steps *also* always fail:
##[error]Artifact uitests-android-nativeaot-failure-results not found for build 174635. Please ensure you have published artifacts in any previous phases of the current build.
Update `ApplicationData.GetRoamingFolder()` to explicitly create
`Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)`.
(Apparently this isn't created by default under NativeAOT?)
This avoids a startup assertion:
I NativeAotFromAndroid: App failed to initialize: System.IO.DirectoryNotFoundException: IO_PathNotFound_Path, /data/user/0/uno.platform.samplesapp.skia/files/.config/b63c4306-f361-42d0-bc1d-5be385a95c78.txt
I NativeAotFromAndroid: at System.IO.FileSystem.DeleteFile(String) + 0xe7
I NativeAotFromAndroid: at SamplesApp.App.<AssertApplicationData>g__AssertCanCreateFile|32_3(StorageFolder) + 0x10c
I NativeAotFromAndroid: at SamplesApp.App.AssertApplicationData() + 0xa5
I NativeAotFromAndroid: at SamplesApp.App..ctor() + 0x2a3
I NativeAotFromAndroid: at SamplesApp.Droid.Application.<>c.<.ctor>b__0_0() + 0x18
I NativeAotFromAndroid: at Microsoft.UI.Xaml.NativeApplication.<OnActivityStarted>b__7_0() + 0x12
I NativeAotFromAndroid: at Uno.UI.Runtime.Skia.Android.AndroidHost.<Run>g__CreateApp|2_10(ApplicationInitializationCallbackParams _) + 0xf
I NativeAotFromAndroid: at Microsoft.UI.Xaml.Application.StartPartial(ApplicationInitializationCallback callback) + 0xb5
I NativeAotFromAndroid: at Uno.UI.Runtime.Skia.Android.AndroidHost.Run() + 0x306
Enable NativeAOT builds by updating
`SamplesApp.Skia.netcoremobile.csproj` to set the [`$(PublishAot)`][1]
property to the `$(SkiaPublishAot)` MSBuild property. This allows
us to build a single project for NativeAOT --
`SamplesApp.Skia.netcoremobile.csproj` -- *without* trying to build
every referenced project for NativeAOT, which is what happens if you
instead try `dotnet publish -p:PublishAot=true …`, which fails:
% dotnet build -c Release -p:PublishAot=true src/SamplesApp/SamplesApp.Skia.netcoremobile/SamplesApp.Skia.netcoremobile.csproj -bl
…
…/Microsoft.NET.Sdk.FrameworkReferenceResolution.targets(121,5): error NETSDK1207: Ahead-of-time compilation is not supported for the target framework.
% dotnet publish src/SamplesApp/SamplesApp.Skia.netcoremobile/SamplesApp.Skia.netcoremobile.csproj \
-c Release -r android-x64 -f net10.0-android -p:UnoTargetFrameworkOverride=net10.0-android -p:SkiaPublishAot=true -bl
# succeeds… after 15 minutes…
This also requires updating `$(NoWarn)` to ignore the hundreds of
IL trimmer warnings. We're just trying to see where things stand for now.
Additionally, we need to publish with `-r android-x64` in order to
avoid the build error:
…/Microsoft.NETCore.Native.Publish.targets(92,5): error MSB3030: Could not copy the file "bin/Release/net10.0-android/native/SamplesApp.so" because it was not found.
Because it's `bin/Release/net10.0-android/android-{arm64,x64}/native/SamplesApp.so`!
Oddly, using `-r android-x64` still results in *both* ABIs being
included, which appears to be a unoplatform/uno "bug":
% unzip -l src/SamplesApp/SamplesApp.Skia.netcoremobile/bin/Release/net10.0-android/android-x64/publish/uno.platform.samplesapp.skia-Signed.apk | grep SamplesApp.so
763563120 08-28-2025 19:47 lib/x86_64/libSamplesApp.so
757982224 08-28-2025 19:48 lib/arm64-v8a/libSamplesApp.so
The need for `-r android-x64` may be related to dotnet/android#10457.
.NET Crypto support isn't propertly initialized in Android+NativeAOT
apps in .NET 10 RC1; see dotnet/android#10463. This may be fixed in
dotnet/android#10461, but in the meantime we can manually call
`AndroidCryptoNative_InitLibraryOnLoad()` so that methods such as
`SHA1.Create()` won't throw.
Exclude tests which don't pass under NativeAOT. These fall into
three major categories:
1. Failures that don't make sense, likely due to unknown bugs within
the NativeAOT toolchain itself, failing due to
`TargetParameterCountException` or `InvalidOperationException`
described below. Some of these are worked around (see below),
while others are ignored via e.g.:
[Ignore("DataRowAttribute.GetData() wraps data in an extra array under NativeAOT; not yet understood why.")]
2. Failures which are due to `AwesomeAssertions` using
`MethodInfo.MakeGenericMethod()`; see
AwesomeAssertions/AwesomeAssertions#290:
Unhandled exception. System.NotSupportedException: 'AwesomeAssertions.Equivalency.Steps.GenericEnumerableEquivalencyStep.HandleImpl[System.Int32](AwesomeAssertions.Equivalency.Steps.EnumerableEquivalencyValidator,System.Object[],System.Collections.Generic.IEnumerable`1[System.Int32])' is missing native code. MethodInfo.MakeGenericMethod() is not compatible with AOT compilation. Inspect and fix AOT related warnings that were generated when the app was published. For more information see https://aka.ms/nativeaot-compatibility
at System.Reflection.Runtime.MethodInfos.RuntimeNamedMethodInfo`1.GetUncachedMethodInvoker(RuntimeTypeInfo[], MemberInfo) + 0x2a
at System.Reflection.Runtime.MethodInfos.RuntimeMethodInfo.Invoke(Object, BindingFlags, Binder, Object[], CultureInfo) + 0x3f
at AwesomeAssertions.Equivalency.Steps.GenericEnumerableEquivalencyStep.Handle(Comparands, IEquivalencyValidationContext, IValidateChildNodeEquivalency) + 0x21a
at AwesomeAssertions.Equivalency.EquivalencyValidator.TryToProveNodesAreEquivalent(Comparands, IEquivalencyValidationContext) + 0x129
at AwesomeAssertions.Equivalency.EquivalencyValidator.AssertEquality(Comparands, EquivalencyValidationContext) + 0x43
at AwesomeAssertions.Collections.GenericCollectionAssertions`3.BeEquivalentTo[TExpectation](IEnumerable`1, Func`2, String, Object[]) + 0x1f9
Basically, the `.BeBeEquivalentTo()` extension method cannot
currently work in NativeAOT environments.
These are ignored via:
[Ignore(".BeEquivalentTo() unsupported under NativeAOT; see: AwesomeAssertions/AwesomeAssertions#290")]
3. Other limitations within the NativeAOT environment. For example,
Android+NativeAOT does not yet have a GC bridge, meaning every
`Java.Lang.Object` subclass instance is *never* collected by the
GC unless explicitly `.Dispose()`d
Update `UnitTestsControl.cs` to try to work around some NativeAOT
"weirdness" and provide better error messages when tests fail in
certain scenarios. Previously, many tests would fail with one of:
System.Reflection.TargetParameterCountException: Arg_ParmCnt
at System.Reflection.DynamicInvokeInfo.ThrowForArgCountMismatch() + 0x83
at System.Reflection.DynamicInvokeInfo.Invoke(Object, IntPtr, Object[], BinderBundle, Boolean) + 0x136
at Internal.Reflection.Execution.MethodInvokers.InstanceMethodInvoker.Invoke(Object, Object[], BinderBundle, Boolean) + 0x4f
at Internal.Reflection.Core.Execution.MethodBaseInvoker.Invoke(Object, Object[], Binder, BindingFlags, CultureInfo) + 0x48
at System.Reflection.Runtime.MethodInfos.RuntimeMethodInfo.Invoke(Object, BindingFlags, Binder, Object[], CultureInfo) + 0x70
at Uno.UI.Samples.Tests.UnitTestsControl.<>c__DisplayClass67_2.<<ExecuteTestsForInstance>g__InvokeTestMethod|5>d.MoveNext() + 0x4b9
or:
System.InvalidOperationException: Missing parameter does not have default value
at Uno.UI.Samples.Tests.UnitTestsControl.ExpandArgumentsWithDefaultValues(Object[] methodArguments, ParameterInfo[] methodParameters) + 0x138
at Uno.UI.Samples.Tests.UnitTestsControl.<>c__DisplayClass67_4.<<ExecuteTestsForInstance>b__12>d.MoveNext() + 0x92
--- End of stack trace from previous location ---
at Uno.UI.Samples.Tests.UnitTestsControl.<>c__DisplayClass67_2.<<ExecuteTestsForInstance>g__InvokeTestMethod|5>d.MoveNext() + 0x549
A problem is that the reported test that was failing would be
reported as e.g. `When_Add_Remove(System.Object[])`, which is a test
method which *does not exist*; it's actually
`When_Add_Remove(object, int, LeakTestStyles, RuntimeTestPlatforms)`.
Additionally, which parameter doesn't have a default value?
Or how does the parameter count not match? Or…
An intermediate form of this commit would wrap the
`TargetParameterCountException` in an `InvalidOperationException`,
resulting in messages such as:
System.InvalidOperationException: Exception thrown while invoking Uno.UI.Tests.Windows_Globalization.Given_NumeralSystemTranslator.When_NumeralSystemIsMtei(System.String, System.String) with arguments { System.Object[]{ 1 as System.String, ꯱ as System.String } }.
---> System.Reflection.TargetParameterCountException: Parameter count mismatch.
Note that `When_NumeralSystemIsMtei(System.String, System.String)`
takes two arguments, but it's given *one* argument with value
`{{ System.Object[]{ 1 as System.String, ꯱ as System.String } }}`,
i.e. *the values are there*, just "wrapped" in an extra array.
Update `UnitTestsControl.InvokeTestMethod()` to always call
`ExpandArgumentsWithDefaultValues()` as part of test method
invocation, so that we have a centralized place to look for such
"extra array wrapping". Update `ExpandArgumentsWithDefaultValues()`
so that if it encounters an array for a parameter type which isn't
an array, the array is expanded as additional arguments.
Log various "weird" scenarios via `Console.WriteLine()` to make it
easier to get a "complete" listing of such failing methods by using
`adb logcat` output.
TODO? the migration to AwesomeAssertions in 80c0705 results in some
"bizarre" failure messages, e.g.
Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException: Expected value to be less than or equal to 81 because
TextBox/SingleTextBox;Microsoft.UI.Xaml.VisualStateManager;Microsoft.UI.Xaml.Controls.Grid;…;ElementStub/HorizontalScrollBar;…
which is *truncated* at *4000* characters. *Something* is wonky there.
[0]: https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/
[1]: https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/?tabs=windows%2Cnet8#publish-native-aot-using-the-cli
[2]: https://developer.android.com/guide/topics/manifest/application-element#debug
[3]: dotnet/java-interop@90ac2021 parent ce7b4c4 commit 92676ea
File tree
30 files changed
+483
-20
lines changed- build
- ci/tests
- test-scripts
- src
- SamplesApp
- SamplesApp.Skia.netcoremobile
- Android
- SamplesApp.UITests/Windows_UI_Xaml_Input
- Uno.UI.RuntimeTests/Tests
- Uno_UI_Extensions
- Uno_UI_Xaml_Core
- Windows_Globalization
- Windows_Storage
- Windows_UI_Xaml_Controls
- Windows_UI_Xaml_Media_Animation
- Windows_UI_Xaml
- Uno.UI.Tests
- Foundation
- Windows_Globalization
- Windows_UI_Input
- Windows_UI_XAML_Controls/GridTests
- Windows_UI_Xaml_Markup/XamlReaderTests
- Uno.UWP/Storage
30 files changed
+483
-20
lines changedLines changed: 61 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 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 | + | |
Lines changed: 131 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
75 | 75 | | |
76 | 76 | | |
77 | 77 | | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
78 | 95 | | |
79 | 96 | | |
80 | 97 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
157 | 157 | | |
158 | 158 | | |
159 | 159 | | |
160 | | - | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
161 | 167 | | |
162 | 168 | | |
163 | 169 | | |
164 | 170 | | |
165 | 171 | | |
166 | 172 | | |
167 | | - | |
168 | | - | |
169 | | - | |
170 | 173 | | |
171 | 174 | | |
172 | | - | |
173 | 175 | | |
174 | 176 | | |
175 | 177 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
55 | 55 | | |
56 | 56 | | |
57 | 57 | | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
58 | 61 | | |
59 | 62 | | |
60 | 63 | | |
| |||
Lines changed: 14 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
116 | 116 | | |
117 | 117 | | |
118 | 118 | | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
119 | 125 | | |
120 | 126 | | |
121 | 127 | | |
| |||
126 | 132 | | |
127 | 133 | | |
128 | 134 | | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
129 | 143 | | |
Lines changed: 6 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
32 | 32 | | |
33 | 33 | | |
34 | 34 | | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
35 | 38 | | |
36 | 39 | | |
37 | 40 | | |
| |||
53 | 56 | | |
54 | 57 | | |
55 | 58 | | |
| 59 | + | |
| 60 | + | |
56 | 61 | | |
57 | 62 | | |
58 | 63 | | |
| |||
291 | 296 | | |
292 | 297 | | |
293 | 298 | | |
| 299 | + | |
294 | 300 | | |
295 | 301 | | |
296 | 302 | | |
| |||
Lines changed: 9 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
35 | 35 | | |
36 | 36 | | |
37 | 37 | | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
38 | 41 | | |
39 | 42 | | |
40 | 43 | | |
| |||
67 | 70 | | |
68 | 71 | | |
69 | 72 | | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
70 | 76 | | |
71 | 77 | | |
72 | 78 | | |
| |||
101 | 107 | | |
102 | 108 | | |
103 | 109 | | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
104 | 113 | | |
105 | 114 | | |
106 | 115 | | |
| |||
0 commit comments