[native] NativeAOT runtime - #10461

Merged
grendello merged 26 commits into
mainfrom
dev/grendel/naot-runtime
Sep 12, 2025
Merged

[native] NativeAOT runtime#10461
grendello merged 26 commits into
mainfrom
dev/grendel/naot-runtime

Conversation

@grendello

@grendellogrendello commented Sep 1, 2025

Copy link
Copy Markdown
Contributor

Fixes: #10455

Implements a minimal Android host for NativeAOT applications. The goal is to create
a runtime environment that is as compatible with Xamarin.Android / .NET for Android
applications using MonoVM or CoreCLR as possible. This PR implements the very basics:

  • Support for pre-loading of JNI libraries
  • Support for setting of the application environment variables (via <AndroidEnvironment>)
  • Support for the CoreCLR GC bridge

An important element that's currently missing is linking libc++ statically into the runtime, instead we include the shared libc++ from the NDK. The reason for this is that
NativeAOT build task references a compatibility C++ library which produces conflicts with
the static libc++ one when attempting to link. This issue will be fixed in a separate
PR.

@grendello
grendelloforce-pushed the dev/grendel/naot-runtime branch 2 times, most recently from 443bfb4 to 03d77e5CompareSeptember 2, 2025 14:15
@grendello

Copy link
Copy Markdown
ContributorAuthor

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@grendello
grendelloforce-pushed the dev/grendel/naot-runtime branch from 03d77e5 to 04ca2d9CompareSeptember 3, 2025 12:17
@jonpryor

Copy link
Copy Markdown
Contributor

Requested feature addition: CoreCLR has logic to use libSystem.Security.Cryptography.Native.Android.a":

It would be handy if similar integration could be done with NativeAOT, such that AndroidCryptoNative_InitLibraryOnLoad() is invoked during app startup when crypto support is required. This would remove the need for the kludge described here:

@grendello

Copy link
Copy Markdown
ContributorAuthor

@jonpryor I plan to add support for this, definitely. It'll be the same mechanism as with CoreCLR, just need to implement a bit more things before that

@jkoritzinsky

Copy link
Copy Markdown
Member

@grendello@jonathanpeppers should I close #10402 if this is intended to replace it?

@grendello

Copy link
Copy Markdown
ContributorAuthor

@jkoritzinsky no, please keep it open. I'm not going to put everything in this PR that's in #10402. Once this one is done, you can rebase yours and apply the remaining changes + finetuning + whatnot.

I should be done with this PR next week (need to add two more things)

@grendello
grendelloforce-pushed the dev/grendel/naot-runtime branch from bb04b1e to e6d636aCompareSeptember 9, 2025 13:17
@grendello
grendello marked this pull request as ready for review September 10, 2025 14:08

@jonathanpeppersjonathanpeppers left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The NativeAOT test on Windows is failing with:

 (_CollectAssembliesToCompress target) -> C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: System.InvalidOperationException: Assembly compression info not found for key '__CompressedAssembliesInfo:C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj'. Compression will not be performed. [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.GetCompressedAssemblyInfo() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.RunTask() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Microsoft.Android.Build.Tasks.AndroidTask.Execute() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]

I don't see any new RegisterTaskObject() calls.

So, is the problem specific to Windows? Or maybe the test is doing multiple RIDs?

@@ -110,9 +115,96 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android.
<ResolvedFileToPublish Include="$(_NdkSysrootDir)libc++_shared.so" RuntimeIdentifier="$(RuntimeIdentifier)" />

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be removed? Are we statically linking libc++?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're not right now because NativeAOT has a compatibility library that conflicts with static libc++. I want to explore a fix for this in a separate PR, since this one is too big already.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree it makes sense to split that off into separate PR. I suppose you can remove the shim libc++ from the list and then add the real one instead.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to keep both references to libc++_shared.so in this PR as we both need to link against it (the <LinkerArg> item) and package it (the ResolvedFileToPublish item)

Comment on lines +86 to +88
if (gref_gc_threshold != int.MaxValue) {
gref_gc_threshold = checked((gref_gc_threshold * 9) / 10);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this taking 90% of this number for all runtimes? Do we need this change?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other runtimes set gref_gc_threshold through different code path but it's still the same value (90% of the max GREF count).

@filipnavara

Copy link
Copy Markdown
Member

This mostly seems to work, except few build issues and excessive logging.

When doing dotnet publish the build configuration is automatically set to Release and that in turn sets EmbedAssembliesIntoApk=true, which later leads to this build error:

 D:\dotnet-android\android\bin\Debug\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009:
System.InvalidOperationException: Assembly compression info not found for key '__CompressedAssembliesInfo:D:\emcli
ent\MailClient.Mobile\MailClient.Mobile.Android\MailClient.Mobile.Android.csproj'. Compression will not be perform
ed.
at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.GetCompressedAssemblyInfo()
at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.RunTask()
at Microsoft.Android.Build.Tasks.AndroidTask.Execute()

Since NativeAOT doesn't operate with assemblies it seems safe to workaround it with -p:EmbedAssembliesIntoApk=false.

When doing multi-RID builds (again, default) I constantly got a variation of this error (for either RID at random):

 MailClient.Mobile.Android failed with 2 error(s) (35.1s)
ILLink : error IL1011: Failed to write 'obj\Release\net10.0-android\android-x64\linked\Java.Interop.dll'.
C:\Users\filip\.nuget\packages\microsoft.net.illink.tasks\10.0.0-rc.2.25427.104\build\Microsoft.NET.ILLink.targets(103,5): error NETSDK1144: Optimizing assemblies for size failed.

To get past that I used -r android-x64 and the app was built successfully.

The startup took well over a minute and the whole log was spammed with the following:

09-10 22:00:00.319 5559 5576 W monodroid-assembly: virtual bool BridgeProcessing::maybe_call_gc_user_peerable_add_managed_reference(JNIEnv *, jobject, jobject)
09-10 22:00:00.319 5559 5576 W monodroid-assembly: virtual bool BridgeProcessing::maybe_call_gc_user_peerable_add_managed_reference(JNIEnv *, jobject, jobject)
...
09-10 22:00:00.340 5559 5576 W monodroid-assembly: virtual bool BridgeProcessing::maybe_call_gc_user_peerable_clear_managed_references(JNIEnv *, jobject)
09-10 22:00:00.340 5559 5576 W monodroid-assembly: virtual bool BridgeProcessing::maybe_call_gc_user_peerable_clear_managed_references(JNIEnv *, jobject)

Presumably the slow startup was caused just by the logging but it needs to be verified. Experiments with the previous PR showed similar behavior due to incorrectly set gref_gc_threshold which caused excessive GC triggers, but this seems to be handled correctly in this PR.

@grendello
grendelloforce-pushed the dev/grendel/naot-runtime branch from 493cbd8 to 96113beCompareSeptember 11, 2025 09:17
@grendello

Copy link
Copy Markdown
ContributorAuthor

The NativeAOT test on Windows is failing with:

 (_CollectAssembliesToCompress target) -> C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: System.InvalidOperationException: Assembly compression info not found for key '__CompressedAssembliesInfo:C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj'. Compression will not be performed. [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.GetCompressedAssemblyInfo() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.RunTask() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Microsoft.Android.Build.Tasks.AndroidTask.Execute() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]

I don't see any new RegisterTaskObject() calls.

So, is the problem specific to Windows? Or maybe the test is doing multiple RIDs?

The problem was Debug vs Release. I tested only in the former and disabled only part of compressed assembly support. It should work now for Release too.

@filipnavara

filipnavara commented Sep 11, 2025

Copy link
Copy Markdown
Member

The GREF threshold initialization still seems to be wrong:

...
09-11 15:18:56.370 10555 10555 I NativeAotFromAndroid: APPLICATION Initialize END
09-11 15:18:56.372 10555 10555 D monodroid-gc: 83 outstanding GREFs. Performing a full GC!
09-11 15:18:56.376 10555 10617 D monodroid-gc: 84 outstanding GREFs. Performing a full GC!
09-11 15:18:56.392 10555 10555 D monodroid-gc: 59 outstanding GREFs. Performing a full GC!
09-11 15:18:56.400 10555 10617 D monodroid-gc: 60 outstanding GREFs. Performing a full GC!
09-11 15:18:56.416 10555 10573 I ient.mailclient: Explicit concurrent mark compact GC freed 1132KB AllocSpace bytes, 0(0B) LOS objects, 49% free, 2752KB/5504KB, paused 2.622ms,1.462ms total 35.554ms
09-11 15:18:56.419 10555 10555 D monodroid-gc: 86 outstanding GREFs. Performing a full GC!
09-11 15:18:56.419 10555 10617 D monodroid-gc: 86 outstanding GREFs. Performing a full GC!
09-11 15:18:56.448 10555 10573 I ient.mailclient: Explicit concurrent mark compact GC freed 152KB AllocSpace bytes, 0(0B) LOS objects, 49% free, 2728KB/5456KB, paused 1.917ms,2.356ms total 23.981ms
09-11 15:18:56.455 10555 10555 D monodroid-gc: 87 outstanding GREFs. Performing a full GC!
09-11 15:18:56.482 10555 10573 I ient.mailclient: Explicit concurrent mark compact GC freed 160KB AllocSpace bytes, 0(0B) LOS objects, 49% free, 2696KB/5392KB, paused 1.136ms,1.627ms total 21.812ms
09-11 15:18:56.488 10555 10555 D monodroid-gc: 88 outstanding GREFs. Performing a full GC!
09-11 15:18:56.515 10555 10573 I ient.mailclient: Explicit concurrent mark compact GC freed 96KB AllocSpace bytes, 0(0B) LOS objects, 49% free, 2696KB/5392KB, paused 730us,1.376ms total 22.020ms
09-11 15:18:56.517 10555 10555 D monodroid-gc: 89 outstanding GREFs. Performing a full GC!

Essentially every allocation of bridge object triggers a GC.

Probably a missing call to AndroidSystem::init_max_gref_count (); somewhere.

jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 11, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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, we've decided to introduce a new
stage to build and run these unit tests within an Android+NativeAOT
environment.
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.
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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 11, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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.
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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 12, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
Builds, installs, does nothing yet.
Linking seems to work, not tested yet
NativeAOT sample now links without errors about missing symbols (added
linker options for it to fail if missing symbols are detected)
@grendello
grendelloforce-pushed the dev/grendel/naot-runtime branch from 251297f to 68b9a88CompareSeptember 12, 2025 07:57
Comment on lines +105 to +106
// TODO: this shouldn't be public
public class AndroidObjectReferenceManager : JniRuntime.JniObjectReferenceManager {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the only thing we'll need to fix ASAP next week.

@grendello
grendello merged commit 869b0e0 into mainSep 12, 2025
59 checks passed
@grendello
grendello deleted the dev/grendel/naot-runtime branch September 12, 2025 14:14
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 15, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
grendello added a commit that referenced this pull request Sep 18, 2025
Context: #10461
A follow up to #10461 which adds some more compatibility with the "standard"
MonoVM and CoreCLR runtime hosts, namely:
* Support for setting environment variables via the environment file
(`<AndroidEnvironment>` MSBuild item)
* Support for setting **local** (that is, application level, not system
level) system properties
In addition to the above, it makes NativeAOT use static libc++ instead of the
shared one, which not only shrinks the package size (`libc++_shared.so` is
around 9mb), but it also makes startup slightly faster.
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 25, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 25, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 25, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 30, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 2, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 3, 2025
…droid
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 …`:
% 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.
Update `UnitTestsControl.cs` to provide better error messages when
tests fail in certain scenarios. Previously, many (many!) tests
would fail:
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
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
The 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…
Exclude tests which don't pass under NativeAOT. These fall into two
major categories:
1. Failures that don't make sense, failing due to the above two
mentioned exceptions.
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")]
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 3, 2025
…droid
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 …`:
% 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.
Update `UnitTestsControl.cs` to provide better error messages when
tests fail in certain scenarios. Previously, many (many!) tests
would fail:
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
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
The 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…
Exclude tests which don't pass under NativeAOT. These fall into two
major categories:
1. Failures that don't make sense, failing due to the above two
mentioned exceptions.
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")]
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 3, 2025
…droid
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 …`:
% 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.
Update `UnitTestsControl.cs` to provide better error messages when
tests fail in certain scenarios. Previously, many (many!) tests
would fail:
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
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
The 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…
Exclude tests which don't pass under NativeAOT. These fall into two
major categories:
1. Failures that don't make sense, failing due to the above two
mentioned exceptions.
These are ignored via:
[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")]
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 3, 2025
…droid
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 …`:
% 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.
Update `UnitTestsControl.cs` to provide better error messages when
tests fail in certain scenarios. Previously, many (many!) tests
would fail:
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
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
The 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…
Exclude tests which don't pass under NativeAOT. These fall into two
major categories:
1. Failures that don't make sense, failing due to the above two
mentioned exceptions.
These are ignored via:
[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")]
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 4, 2025
…droid
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 …`:
% 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.
Update `UnitTestsControl.cs` to provide better error messages when
tests fail in certain scenarios. Previously, many (many!) tests
would fail:
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
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
The 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…
Exclude tests which don't pass under NativeAOT. These fall into two
major categories:
1. Failures that don't make sense, failing due to the above two
mentioned exceptions.
These are ignored via:
[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")]
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 6, 2025
…droid
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 …`:
% 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 two
major categories:
1. Failures that don't make sense, failing due to
`TargetParameterCountException` or `InvalidOperationException`
described below.
These are ignored via:
[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")]
Update `UnitTestsControl.cs` to try to work around NativeAOT
"weirdness" and provide better error messages when
tests fail in certain scenarios. Previously, many (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
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.
Additionally, if a `TargetInvocationException` is thrown when the
the method arguments count and parameters count match, throw an
`AssertInconclusiveException`.
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.
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 6, 2025
…droid
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 …`:
% 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 two
major categories:
1. Failures that don't make sense, failing due to
`TargetParameterCountException` or `InvalidOperationException`
described below.
These are ignored via:
[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")]
Update `UnitTestsControl.cs` to try to work around NativeAOT
"weirdness" and provide better error messages when
tests fail in certain scenarios. Previously, many (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
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.
Additionally, if a `TargetInvocationException` is thrown when the
the method arguments count and parameters count match, throw an
`AssertInconclusiveException`.
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.
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 6, 2025
…droid
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 …`:
% 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
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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 7, 2025
…droid
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 …`:
% 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
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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 10, 2025
…droid
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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 10, 2025
…droid
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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 10, 2025
…droid
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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 10, 2025
…droid
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@90ac202
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Oct 13, 2025
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Create our initial native library for NativeAOT

5 participants

@grendello@jonpryor@jkoritzinsky@filipnavara@jonathanpeppers
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

[native] NativeAOT runtime - #10461

Merged
grendello merged 26 commits into
mainfrom
dev/grendel/naot-runtime
Sep 12, 2025
Merged

[native] NativeAOT runtime#10461
grendello merged 26 commits into
mainfrom
dev/grendel/naot-runtime

Conversation

@grendello

@grendellogrendello commented Sep 1, 2025

Copy link
Copy Markdown
Contributor

Fixes: #10455

Implements a minimal Android host for NativeAOT applications. The goal is to create
a runtime environment that is as compatible with Xamarin.Android / .NET for Android
applications using MonoVM or CoreCLR as possible. This PR implements the very basics:

  • Support for pre-loading of JNI libraries
  • Support for setting of the application environment variables (via <AndroidEnvironment>)
  • Support for the CoreCLR GC bridge

An important element that's currently missing is linking libc++ statically into the runtime, instead we include the shared libc++ from the NDK. The reason for this is that
NativeAOT build task references a compatibility C++ library which produces conflicts with
the static libc++ one when attempting to link. This issue will be fixed in a separate
PR.

@grendello
grendelloforce-pushed the dev/grendel/naot-runtime branch 2 times, most recently from 443bfb4 to 03d77e5CompareSeptember 2, 2025 14:15
@grendello

Copy link
Copy Markdown
ContributorAuthor

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@grendello
grendelloforce-pushed the dev/grendel/naot-runtime branch from 03d77e5 to 04ca2d9CompareSeptember 3, 2025 12:17
@jonpryor

Copy link
Copy Markdown
Contributor

Requested feature addition: CoreCLR has logic to use libSystem.Security.Cryptography.Native.Android.a":

It would be handy if similar integration could be done with NativeAOT, such that AndroidCryptoNative_InitLibraryOnLoad() is invoked during app startup when crypto support is required. This would remove the need for the kludge described here:

@grendello

Copy link
Copy Markdown
ContributorAuthor

@jonpryor I plan to add support for this, definitely. It'll be the same mechanism as with CoreCLR, just need to implement a bit more things before that

@jkoritzinsky

Copy link
Copy Markdown
Member

@grendello@jonathanpeppers should I close #10402 if this is intended to replace it?

@grendello

Copy link
Copy Markdown
ContributorAuthor

@jkoritzinsky no, please keep it open. I'm not going to put everything in this PR that's in #10402. Once this one is done, you can rebase yours and apply the remaining changes + finetuning + whatnot.

I should be done with this PR next week (need to add two more things)

@grendello
grendelloforce-pushed the dev/grendel/naot-runtime branch from bb04b1e to e6d636aCompareSeptember 9, 2025 13:17
@grendello
grendello marked this pull request as ready for review September 10, 2025 14:08

@jonathanpeppersjonathanpeppers left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The NativeAOT test on Windows is failing with:

 (_CollectAssembliesToCompress target) -> C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: System.InvalidOperationException: Assembly compression info not found for key '__CompressedAssembliesInfo:C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj'. Compression will not be performed. [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.GetCompressedAssemblyInfo() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.RunTask() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Microsoft.Android.Build.Tasks.AndroidTask.Execute() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]

I don't see any new RegisterTaskObject() calls.

So, is the problem specific to Windows? Or maybe the test is doing multiple RIDs?

@@ -110,9 +115,96 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android.
<ResolvedFileToPublish Include="$(_NdkSysrootDir)libc++_shared.so" RuntimeIdentifier="$(RuntimeIdentifier)" />

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be removed? Are we statically linking libc++?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're not right now because NativeAOT has a compatibility library that conflicts with static libc++. I want to explore a fix for this in a separate PR, since this one is too big already.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree it makes sense to split that off into separate PR. I suppose you can remove the shim libc++ from the list and then add the real one instead.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to keep both references to libc++_shared.so in this PR as we both need to link against it (the <LinkerArg> item) and package it (the ResolvedFileToPublish item)

Comment on lines +86 to +88
if (gref_gc_threshold != int.MaxValue) {
gref_gc_threshold = checked((gref_gc_threshold * 9) / 10);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this taking 90% of this number for all runtimes? Do we need this change?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other runtimes set gref_gc_threshold through different code path but it's still the same value (90% of the max GREF count).

@filipnavara

Copy link
Copy Markdown
Member

This mostly seems to work, except few build issues and excessive logging.

When doing dotnet publish the build configuration is automatically set to Release and that in turn sets EmbedAssembliesIntoApk=true, which later leads to this build error:

 D:\dotnet-android\android\bin\Debug\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009:
System.InvalidOperationException: Assembly compression info not found for key '__CompressedAssembliesInfo:D:\emcli
ent\MailClient.Mobile\MailClient.Mobile.Android\MailClient.Mobile.Android.csproj'. Compression will not be perform
ed.
at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.GetCompressedAssemblyInfo()
at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.RunTask()
at Microsoft.Android.Build.Tasks.AndroidTask.Execute()

Since NativeAOT doesn't operate with assemblies it seems safe to workaround it with -p:EmbedAssembliesIntoApk=false.

When doing multi-RID builds (again, default) I constantly got a variation of this error (for either RID at random):

 MailClient.Mobile.Android failed with 2 error(s) (35.1s)
ILLink : error IL1011: Failed to write 'obj\Release\net10.0-android\android-x64\linked\Java.Interop.dll'.
C:\Users\filip\.nuget\packages\microsoft.net.illink.tasks\10.0.0-rc.2.25427.104\build\Microsoft.NET.ILLink.targets(103,5): error NETSDK1144: Optimizing assemblies for size failed.

To get past that I used -r android-x64 and the app was built successfully.

The startup took well over a minute and the whole log was spammed with the following:

09-10 22:00:00.319 5559 5576 W monodroid-assembly: virtual bool BridgeProcessing::maybe_call_gc_user_peerable_add_managed_reference(JNIEnv *, jobject, jobject)
09-10 22:00:00.319 5559 5576 W monodroid-assembly: virtual bool BridgeProcessing::maybe_call_gc_user_peerable_add_managed_reference(JNIEnv *, jobject, jobject)
...
09-10 22:00:00.340 5559 5576 W monodroid-assembly: virtual bool BridgeProcessing::maybe_call_gc_user_peerable_clear_managed_references(JNIEnv *, jobject)
09-10 22:00:00.340 5559 5576 W monodroid-assembly: virtual bool BridgeProcessing::maybe_call_gc_user_peerable_clear_managed_references(JNIEnv *, jobject)

Presumably the slow startup was caused just by the logging but it needs to be verified. Experiments with the previous PR showed similar behavior due to incorrectly set gref_gc_threshold which caused excessive GC triggers, but this seems to be handled correctly in this PR.

@grendello
grendelloforce-pushed the dev/grendel/naot-runtime branch from 493cbd8 to 96113beCompareSeptember 11, 2025 09:17
@grendello

Copy link
Copy Markdown
ContributorAuthor

The NativeAOT test on Windows is failing with:

 (_CollectAssembliesToCompress target) -> C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: System.InvalidOperationException: Assembly compression info not found for key '__CompressedAssembliesInfo:C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj'. Compression will not be performed. [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.GetCompressedAssemblyInfo() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.RunTask() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Microsoft.Android.Build.Tasks.AndroidTask.Execute() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]

I don't see any new RegisterTaskObject() calls.

So, is the problem specific to Windows? Or maybe the test is doing multiple RIDs?

The problem was Debug vs Release. I tested only in the former and disabled only part of compressed assembly support. It should work now for Release too.

@filipnavara

filipnavara commented Sep 11, 2025

Copy link
Copy Markdown
Member

The GREF threshold initialization still seems to be wrong:

...
09-11 15:18:56.370 10555 10555 I NativeAotFromAndroid: APPLICATION Initialize END
09-11 15:18:56.372 10555 10555 D monodroid-gc: 83 outstanding GREFs. Performing a full GC!
09-11 15:18:56.376 10555 10617 D monodroid-gc: 84 outstanding GREFs. Performing a full GC!
09-11 15:18:56.392 10555 10555 D monodroid-gc: 59 outstanding GREFs. Performing a full GC!
09-11 15:18:56.400 10555 10617 D monodroid-gc: 60 outstanding GREFs. Performing a full GC!
09-11 15:18:56.416 10555 10573 I ient.mailclient: Explicit concurrent mark compact GC freed 1132KB AllocSpace bytes, 0(0B) LOS objects, 49% free, 2752KB/5504KB, paused 2.622ms,1.462ms total 35.554ms
09-11 15:18:56.419 10555 10555 D monodroid-gc: 86 outstanding GREFs. Performing a full GC!
09-11 15:18:56.419 10555 10617 D monodroid-gc: 86 outstanding GREFs. Performing a full GC!
09-11 15:18:56.448 10555 10573 I ient.mailclient: Explicit concurrent mark compact GC freed 152KB AllocSpace bytes, 0(0B) LOS objects, 49% free, 2728KB/5456KB, paused 1.917ms,2.356ms total 23.981ms
09-11 15:18:56.455 10555 10555 D monodroid-gc: 87 outstanding GREFs. Performing a full GC!
09-11 15:18:56.482 10555 10573 I ient.mailclient: Explicit concurrent mark compact GC freed 160KB AllocSpace bytes, 0(0B) LOS objects, 49% free, 2696KB/5392KB, paused 1.136ms,1.627ms total 21.812ms
09-11 15:18:56.488 10555 10555 D monodroid-gc: 88 outstanding GREFs. Performing a full GC!
09-11 15:18:56.515 10555 10573 I ient.mailclient: Explicit concurrent mark compact GC freed 96KB AllocSpace bytes, 0(0B) LOS objects, 49% free, 2696KB/5392KB, paused 730us,1.376ms total 22.020ms
09-11 15:18:56.517 10555 10555 D monodroid-gc: 89 outstanding GREFs. Performing a full GC!

Essentially every allocation of bridge object triggers a GC.

Probably a missing call to AndroidSystem::init_max_gref_count (); somewhere.

jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 11, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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, we've decided to introduce a new
stage to build and run these unit tests within an Android+NativeAOT
environment.
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.
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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 11, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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.
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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 12, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
Builds, installs, does nothing yet.
Linking seems to work, not tested yet
NativeAOT sample now links without errors about missing symbols (added
linker options for it to fail if missing symbols are detected)
@grendello
grendelloforce-pushed the dev/grendel/naot-runtime branch from 251297f to 68b9a88CompareSeptember 12, 2025 07:57
Comment on lines +105 to +106
// TODO: this shouldn't be public
public class AndroidObjectReferenceManager : JniRuntime.JniObjectReferenceManager {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the only thing we'll need to fix ASAP next week.

@grendello
grendello merged commit 869b0e0 into mainSep 12, 2025
59 checks passed
@grendello
grendello deleted the dev/grendel/naot-runtime branch September 12, 2025 14:14
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 15, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
grendello added a commit that referenced this pull request Sep 18, 2025
Context: #10461
A follow up to #10461 which adds some more compatibility with the "standard"
MonoVM and CoreCLR runtime hosts, namely:
* Support for setting environment variables via the environment file
(`<AndroidEnvironment>` MSBuild item)
* Support for setting **local** (that is, application level, not system
level) system properties
In addition to the above, it makes NativeAOT use static libc++ instead of the
shared one, which not only shrinks the package size (`libc++_shared.so` is
around 9mb), but it also makes startup slightly faster.
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 25, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 25, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 25, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 30, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 2, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 3, 2025
…droid
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 …`:
% 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.
Update `UnitTestsControl.cs` to provide better error messages when
tests fail in certain scenarios. Previously, many (many!) tests
would fail:
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
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
The 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…
Exclude tests which don't pass under NativeAOT. These fall into two
major categories:
1. Failures that don't make sense, failing due to the above two
mentioned exceptions.
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")]
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 3, 2025
…droid
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 …`:
% 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.
Update `UnitTestsControl.cs` to provide better error messages when
tests fail in certain scenarios. Previously, many (many!) tests
would fail:
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
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
The 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…
Exclude tests which don't pass under NativeAOT. These fall into two
major categories:
1. Failures that don't make sense, failing due to the above two
mentioned exceptions.
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")]
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 3, 2025
…droid
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 …`:
% 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.
Update `UnitTestsControl.cs` to provide better error messages when
tests fail in certain scenarios. Previously, many (many!) tests
would fail:
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
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
The 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…
Exclude tests which don't pass under NativeAOT. These fall into two
major categories:
1. Failures that don't make sense, failing due to the above two
mentioned exceptions.
These are ignored via:
[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")]
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 3, 2025
…droid
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 …`:
% 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.
Update `UnitTestsControl.cs` to provide better error messages when
tests fail in certain scenarios. Previously, many (many!) tests
would fail:
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
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
The 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…
Exclude tests which don't pass under NativeAOT. These fall into two
major categories:
1. Failures that don't make sense, failing due to the above two
mentioned exceptions.
These are ignored via:
[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")]
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 4, 2025
…droid
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 …`:
% 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.
Update `UnitTestsControl.cs` to provide better error messages when
tests fail in certain scenarios. Previously, many (many!) tests
would fail:
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
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
The 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…
Exclude tests which don't pass under NativeAOT. These fall into two
major categories:
1. Failures that don't make sense, failing due to the above two
mentioned exceptions.
These are ignored via:
[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")]
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 6, 2025
…droid
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 …`:
% 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 two
major categories:
1. Failures that don't make sense, failing due to
`TargetParameterCountException` or `InvalidOperationException`
described below.
These are ignored via:
[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")]
Update `UnitTestsControl.cs` to try to work around NativeAOT
"weirdness" and provide better error messages when
tests fail in certain scenarios. Previously, many (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
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.
Additionally, if a `TargetInvocationException` is thrown when the
the method arguments count and parameters count match, throw an
`AssertInconclusiveException`.
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.
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 6, 2025
…droid
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 …`:
% 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 two
major categories:
1. Failures that don't make sense, failing due to
`TargetParameterCountException` or `InvalidOperationException`
described below.
These are ignored via:
[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")]
Update `UnitTestsControl.cs` to try to work around NativeAOT
"weirdness" and provide better error messages when
tests fail in certain scenarios. Previously, many (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
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.
Additionally, if a `TargetInvocationException` is thrown when the
the method arguments count and parameters count match, throw an
`AssertInconclusiveException`.
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.
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 6, 2025
…droid
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 …`:
% 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
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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 7, 2025
…droid
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 …`:
% 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
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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 10, 2025
…droid
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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 10, 2025
…droid
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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 10, 2025
…droid
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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 10, 2025
…droid
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@90ac202
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Oct 13, 2025
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Create our initial native library for NativeAOT

5 participants

@grendello@jonpryor@jkoritzinsky@filipnavara@jonathanpeppers
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[native] NativeAOT runtime - #10461

Merged
grendello merged 26 commits into
mainfrom
dev/grendel/naot-runtime
Sep 12, 2025
Merged

[native] NativeAOT runtime#10461
grendello merged 26 commits into
mainfrom
dev/grendel/naot-runtime

Conversation

@grendello

@grendellogrendello commented Sep 1, 2025

Copy link
Copy Markdown
Contributor

Fixes: #10455

Implements a minimal Android host for NativeAOT applications. The goal is to create
a runtime environment that is as compatible with Xamarin.Android / .NET for Android
applications using MonoVM or CoreCLR as possible. This PR implements the very basics:

  • Support for pre-loading of JNI libraries
  • Support for setting of the application environment variables (via <AndroidEnvironment>)
  • Support for the CoreCLR GC bridge

An important element that's currently missing is linking libc++ statically into the runtime, instead we include the shared libc++ from the NDK. The reason for this is that
NativeAOT build task references a compatibility C++ library which produces conflicts with
the static libc++ one when attempting to link. This issue will be fixed in a separate
PR.

@grendello
grendelloforce-pushed the dev/grendel/naot-runtime branch 2 times, most recently from 443bfb4 to 03d77e5CompareSeptember 2, 2025 14:15
@grendello

Copy link
Copy Markdown
ContributorAuthor

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@grendello
grendelloforce-pushed the dev/grendel/naot-runtime branch from 03d77e5 to 04ca2d9CompareSeptember 3, 2025 12:17
@jonpryor

Copy link
Copy Markdown
Contributor

Requested feature addition: CoreCLR has logic to use libSystem.Security.Cryptography.Native.Android.a":

It would be handy if similar integration could be done with NativeAOT, such that AndroidCryptoNative_InitLibraryOnLoad() is invoked during app startup when crypto support is required. This would remove the need for the kludge described here:

@grendello

Copy link
Copy Markdown
ContributorAuthor

@jonpryor I plan to add support for this, definitely. It'll be the same mechanism as with CoreCLR, just need to implement a bit more things before that

@jkoritzinsky

Copy link
Copy Markdown
Member

@grendello@jonathanpeppers should I close #10402 if this is intended to replace it?

@grendello

Copy link
Copy Markdown
ContributorAuthor

@jkoritzinsky no, please keep it open. I'm not going to put everything in this PR that's in #10402. Once this one is done, you can rebase yours and apply the remaining changes + finetuning + whatnot.

I should be done with this PR next week (need to add two more things)

@grendello
grendelloforce-pushed the dev/grendel/naot-runtime branch from bb04b1e to e6d636aCompareSeptember 9, 2025 13:17
@grendello
grendello marked this pull request as ready for review September 10, 2025 14:08

@jonathanpeppersjonathanpeppers left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The NativeAOT test on Windows is failing with:

 (_CollectAssembliesToCompress target) -> C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: System.InvalidOperationException: Assembly compression info not found for key '__CompressedAssembliesInfo:C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj'. Compression will not be performed. [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.GetCompressedAssemblyInfo() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.RunTask() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Microsoft.Android.Build.Tasks.AndroidTask.Execute() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]

I don't see any new RegisterTaskObject() calls.

So, is the problem specific to Windows? Or maybe the test is doing multiple RIDs?

@@ -110,9 +115,96 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android.
<ResolvedFileToPublish Include="$(_NdkSysrootDir)libc++_shared.so" RuntimeIdentifier="$(RuntimeIdentifier)" />

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be removed? Are we statically linking libc++?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're not right now because NativeAOT has a compatibility library that conflicts with static libc++. I want to explore a fix for this in a separate PR, since this one is too big already.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree it makes sense to split that off into separate PR. I suppose you can remove the shim libc++ from the list and then add the real one instead.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to keep both references to libc++_shared.so in this PR as we both need to link against it (the <LinkerArg> item) and package it (the ResolvedFileToPublish item)

Comment on lines +86 to +88
if (gref_gc_threshold != int.MaxValue) {
gref_gc_threshold = checked((gref_gc_threshold * 9) / 10);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this taking 90% of this number for all runtimes? Do we need this change?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other runtimes set gref_gc_threshold through different code path but it's still the same value (90% of the max GREF count).

@filipnavara

Copy link
Copy Markdown
Member

This mostly seems to work, except few build issues and excessive logging.

When doing dotnet publish the build configuration is automatically set to Release and that in turn sets EmbedAssembliesIntoApk=true, which later leads to this build error:

 D:\dotnet-android\android\bin\Debug\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009:
System.InvalidOperationException: Assembly compression info not found for key '__CompressedAssembliesInfo:D:\emcli
ent\MailClient.Mobile\MailClient.Mobile.Android\MailClient.Mobile.Android.csproj'. Compression will not be perform
ed.
at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.GetCompressedAssemblyInfo()
at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.RunTask()
at Microsoft.Android.Build.Tasks.AndroidTask.Execute()

Since NativeAOT doesn't operate with assemblies it seems safe to workaround it with -p:EmbedAssembliesIntoApk=false.

When doing multi-RID builds (again, default) I constantly got a variation of this error (for either RID at random):

 MailClient.Mobile.Android failed with 2 error(s) (35.1s)
ILLink : error IL1011: Failed to write 'obj\Release\net10.0-android\android-x64\linked\Java.Interop.dll'.
C:\Users\filip\.nuget\packages\microsoft.net.illink.tasks\10.0.0-rc.2.25427.104\build\Microsoft.NET.ILLink.targets(103,5): error NETSDK1144: Optimizing assemblies for size failed.

To get past that I used -r android-x64 and the app was built successfully.

The startup took well over a minute and the whole log was spammed with the following:

09-10 22:00:00.319 5559 5576 W monodroid-assembly: virtual bool BridgeProcessing::maybe_call_gc_user_peerable_add_managed_reference(JNIEnv *, jobject, jobject)
09-10 22:00:00.319 5559 5576 W monodroid-assembly: virtual bool BridgeProcessing::maybe_call_gc_user_peerable_add_managed_reference(JNIEnv *, jobject, jobject)
...
09-10 22:00:00.340 5559 5576 W monodroid-assembly: virtual bool BridgeProcessing::maybe_call_gc_user_peerable_clear_managed_references(JNIEnv *, jobject)
09-10 22:00:00.340 5559 5576 W monodroid-assembly: virtual bool BridgeProcessing::maybe_call_gc_user_peerable_clear_managed_references(JNIEnv *, jobject)

Presumably the slow startup was caused just by the logging but it needs to be verified. Experiments with the previous PR showed similar behavior due to incorrectly set gref_gc_threshold which caused excessive GC triggers, but this seems to be handled correctly in this PR.

@grendello
grendelloforce-pushed the dev/grendel/naot-runtime branch from 493cbd8 to 96113beCompareSeptember 11, 2025 09:17
@grendello

Copy link
Copy Markdown
ContributorAuthor

The NativeAOT test on Windows is failing with:

 (_CollectAssembliesToCompress target) -> C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: System.InvalidOperationException: Assembly compression info not found for key '__CompressedAssembliesInfo:C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj'. Compression will not be performed. [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.GetCompressedAssemblyInfo() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.RunTask() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Microsoft.Android.Build.Tasks.AndroidTask.Execute() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]

I don't see any new RegisterTaskObject() calls.

So, is the problem specific to Windows? Or maybe the test is doing multiple RIDs?

The problem was Debug vs Release. I tested only in the former and disabled only part of compressed assembly support. It should work now for Release too.

@filipnavara

filipnavara commented Sep 11, 2025

Copy link
Copy Markdown
Member

The GREF threshold initialization still seems to be wrong:

...
09-11 15:18:56.370 10555 10555 I NativeAotFromAndroid: APPLICATION Initialize END
09-11 15:18:56.372 10555 10555 D monodroid-gc: 83 outstanding GREFs. Performing a full GC!
09-11 15:18:56.376 10555 10617 D monodroid-gc: 84 outstanding GREFs. Performing a full GC!
09-11 15:18:56.392 10555 10555 D monodroid-gc: 59 outstanding GREFs. Performing a full GC!
09-11 15:18:56.400 10555 10617 D monodroid-gc: 60 outstanding GREFs. Performing a full GC!
09-11 15:18:56.416 10555 10573 I ient.mailclient: Explicit concurrent mark compact GC freed 1132KB AllocSpace bytes, 0(0B) LOS objects, 49% free, 2752KB/5504KB, paused 2.622ms,1.462ms total 35.554ms
09-11 15:18:56.419 10555 10555 D monodroid-gc: 86 outstanding GREFs. Performing a full GC!
09-11 15:18:56.419 10555 10617 D monodroid-gc: 86 outstanding GREFs. Performing a full GC!
09-11 15:18:56.448 10555 10573 I ient.mailclient: Explicit concurrent mark compact GC freed 152KB AllocSpace bytes, 0(0B) LOS objects, 49% free, 2728KB/5456KB, paused 1.917ms,2.356ms total 23.981ms
09-11 15:18:56.455 10555 10555 D monodroid-gc: 87 outstanding GREFs. Performing a full GC!
09-11 15:18:56.482 10555 10573 I ient.mailclient: Explicit concurrent mark compact GC freed 160KB AllocSpace bytes, 0(0B) LOS objects, 49% free, 2696KB/5392KB, paused 1.136ms,1.627ms total 21.812ms
09-11 15:18:56.488 10555 10555 D monodroid-gc: 88 outstanding GREFs. Performing a full GC!
09-11 15:18:56.515 10555 10573 I ient.mailclient: Explicit concurrent mark compact GC freed 96KB AllocSpace bytes, 0(0B) LOS objects, 49% free, 2696KB/5392KB, paused 730us,1.376ms total 22.020ms
09-11 15:18:56.517 10555 10555 D monodroid-gc: 89 outstanding GREFs. Performing a full GC!

Essentially every allocation of bridge object triggers a GC.

Probably a missing call to AndroidSystem::init_max_gref_count (); somewhere.

jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 11, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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, we've decided to introduce a new
stage to build and run these unit tests within an Android+NativeAOT
environment.
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.
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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 11, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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.
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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 12, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
Builds, installs, does nothing yet.
Linking seems to work, not tested yet
NativeAOT sample now links without errors about missing symbols (added
linker options for it to fail if missing symbols are detected)
@grendello
grendelloforce-pushed the dev/grendel/naot-runtime branch from 251297f to 68b9a88CompareSeptember 12, 2025 07:57
Comment on lines +105 to +106
// TODO: this shouldn't be public
public class AndroidObjectReferenceManager : JniRuntime.JniObjectReferenceManager {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the only thing we'll need to fix ASAP next week.

@grendello
grendello merged commit 869b0e0 into mainSep 12, 2025
59 checks passed
@grendello
grendello deleted the dev/grendel/naot-runtime branch September 12, 2025 14:14
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 15, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
grendello added a commit that referenced this pull request Sep 18, 2025
Context: #10461
A follow up to #10461 which adds some more compatibility with the "standard"
MonoVM and CoreCLR runtime hosts, namely:
* Support for setting environment variables via the environment file
(`<AndroidEnvironment>` MSBuild item)
* Support for setting **local** (that is, application level, not system
level) system properties
In addition to the above, it makes NativeAOT use static libc++ instead of the
shared one, which not only shrinks the package size (`libc++_shared.so` is
around 9mb), but it also makes startup slightly faster.
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 25, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 25, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 25, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 30, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 2, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 3, 2025
…droid
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 …`:
% 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.
Update `UnitTestsControl.cs` to provide better error messages when
tests fail in certain scenarios. Previously, many (many!) tests
would fail:
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
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
The 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…
Exclude tests which don't pass under NativeAOT. These fall into two
major categories:
1. Failures that don't make sense, failing due to the above two
mentioned exceptions.
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")]
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 3, 2025
…droid
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 …`:
% 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.
Update `UnitTestsControl.cs` to provide better error messages when
tests fail in certain scenarios. Previously, many (many!) tests
would fail:
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
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
The 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…
Exclude tests which don't pass under NativeAOT. These fall into two
major categories:
1. Failures that don't make sense, failing due to the above two
mentioned exceptions.
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")]
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 3, 2025
…droid
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 …`:
% 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.
Update `UnitTestsControl.cs` to provide better error messages when
tests fail in certain scenarios. Previously, many (many!) tests
would fail:
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
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
The 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…
Exclude tests which don't pass under NativeAOT. These fall into two
major categories:
1. Failures that don't make sense, failing due to the above two
mentioned exceptions.
These are ignored via:
[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")]
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 3, 2025
…droid
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 …`:
% 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.
Update `UnitTestsControl.cs` to provide better error messages when
tests fail in certain scenarios. Previously, many (many!) tests
would fail:
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
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
The 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…
Exclude tests which don't pass under NativeAOT. These fall into two
major categories:
1. Failures that don't make sense, failing due to the above two
mentioned exceptions.
These are ignored via:
[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")]
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 4, 2025
…droid
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 …`:
% 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.
Update `UnitTestsControl.cs` to provide better error messages when
tests fail in certain scenarios. Previously, many (many!) tests
would fail:
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
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
The 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…
Exclude tests which don't pass under NativeAOT. These fall into two
major categories:
1. Failures that don't make sense, failing due to the above two
mentioned exceptions.
These are ignored via:
[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")]
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 6, 2025
…droid
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 …`:
% 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 two
major categories:
1. Failures that don't make sense, failing due to
`TargetParameterCountException` or `InvalidOperationException`
described below.
These are ignored via:
[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")]
Update `UnitTestsControl.cs` to try to work around NativeAOT
"weirdness" and provide better error messages when
tests fail in certain scenarios. Previously, many (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
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.
Additionally, if a `TargetInvocationException` is thrown when the
the method arguments count and parameters count match, throw an
`AssertInconclusiveException`.
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.
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 6, 2025
…droid
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 …`:
% 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 two
major categories:
1. Failures that don't make sense, failing due to
`TargetParameterCountException` or `InvalidOperationException`
described below.
These are ignored via:
[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")]
Update `UnitTestsControl.cs` to try to work around NativeAOT
"weirdness" and provide better error messages when
tests fail in certain scenarios. Previously, many (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
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.
Additionally, if a `TargetInvocationException` is thrown when the
the method arguments count and parameters count match, throw an
`AssertInconclusiveException`.
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.
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 6, 2025
…droid
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 …`:
% 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
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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 7, 2025
…droid
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 …`:
% 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
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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 10, 2025
…droid
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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 10, 2025
…droid
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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 10, 2025
…droid
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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 10, 2025
…droid
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@90ac202
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Oct 13, 2025
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Create our initial native library for NativeAOT

5 participants

@grendello@jonpryor@jkoritzinsky@filipnavara@jonathanpeppers
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[native] NativeAOT runtime - #10461

Merged
grendello merged 26 commits into
mainfrom
dev/grendel/naot-runtime
Sep 12, 2025
Merged

[native] NativeAOT runtime#10461
grendello merged 26 commits into
mainfrom
dev/grendel/naot-runtime

Conversation

@grendello

@grendellogrendello commented Sep 1, 2025

Copy link
Copy Markdown
Contributor

Fixes: #10455

Implements a minimal Android host for NativeAOT applications. The goal is to create
a runtime environment that is as compatible with Xamarin.Android / .NET for Android
applications using MonoVM or CoreCLR as possible. This PR implements the very basics:

  • Support for pre-loading of JNI libraries
  • Support for setting of the application environment variables (via <AndroidEnvironment>)
  • Support for the CoreCLR GC bridge

An important element that's currently missing is linking libc++ statically into the runtime, instead we include the shared libc++ from the NDK. The reason for this is that
NativeAOT build task references a compatibility C++ library which produces conflicts with
the static libc++ one when attempting to link. This issue will be fixed in a separate
PR.

@grendello
grendelloforce-pushed the dev/grendel/naot-runtime branch 2 times, most recently from 443bfb4 to 03d77e5CompareSeptember 2, 2025 14:15
@grendello

Copy link
Copy Markdown
ContributorAuthor

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@grendello
grendelloforce-pushed the dev/grendel/naot-runtime branch from 03d77e5 to 04ca2d9CompareSeptember 3, 2025 12:17
@jonpryor

Copy link
Copy Markdown
Contributor

Requested feature addition: CoreCLR has logic to use libSystem.Security.Cryptography.Native.Android.a":

It would be handy if similar integration could be done with NativeAOT, such that AndroidCryptoNative_InitLibraryOnLoad() is invoked during app startup when crypto support is required. This would remove the need for the kludge described here:

@grendello

Copy link
Copy Markdown
ContributorAuthor

@jonpryor I plan to add support for this, definitely. It'll be the same mechanism as with CoreCLR, just need to implement a bit more things before that

@jkoritzinsky

Copy link
Copy Markdown
Member

@grendello@jonathanpeppers should I close #10402 if this is intended to replace it?

@grendello

Copy link
Copy Markdown
ContributorAuthor

@jkoritzinsky no, please keep it open. I'm not going to put everything in this PR that's in #10402. Once this one is done, you can rebase yours and apply the remaining changes + finetuning + whatnot.

I should be done with this PR next week (need to add two more things)

@grendello
grendelloforce-pushed the dev/grendel/naot-runtime branch from bb04b1e to e6d636aCompareSeptember 9, 2025 13:17
@grendello
grendello marked this pull request as ready for review September 10, 2025 14:08

@jonathanpeppersjonathanpeppers left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The NativeAOT test on Windows is failing with:

 (_CollectAssembliesToCompress target) -> C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: System.InvalidOperationException: Assembly compression info not found for key '__CompressedAssembliesInfo:C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj'. Compression will not be performed. [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.GetCompressedAssemblyInfo() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.RunTask() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Microsoft.Android.Build.Tasks.AndroidTask.Execute() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]

I don't see any new RegisterTaskObject() calls.

So, is the problem specific to Windows? Or maybe the test is doing multiple RIDs?

@@ -110,9 +115,96 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android.
<ResolvedFileToPublish Include="$(_NdkSysrootDir)libc++_shared.so" RuntimeIdentifier="$(RuntimeIdentifier)" />

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be removed? Are we statically linking libc++?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're not right now because NativeAOT has a compatibility library that conflicts with static libc++. I want to explore a fix for this in a separate PR, since this one is too big already.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree it makes sense to split that off into separate PR. I suppose you can remove the shim libc++ from the list and then add the real one instead.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to keep both references to libc++_shared.so in this PR as we both need to link against it (the <LinkerArg> item) and package it (the ResolvedFileToPublish item)

Comment on lines +86 to +88
if (gref_gc_threshold != int.MaxValue) {
gref_gc_threshold = checked((gref_gc_threshold * 9) / 10);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this taking 90% of this number for all runtimes? Do we need this change?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other runtimes set gref_gc_threshold through different code path but it's still the same value (90% of the max GREF count).

@filipnavara

Copy link
Copy Markdown
Member

This mostly seems to work, except few build issues and excessive logging.

When doing dotnet publish the build configuration is automatically set to Release and that in turn sets EmbedAssembliesIntoApk=true, which later leads to this build error:

 D:\dotnet-android\android\bin\Debug\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009:
System.InvalidOperationException: Assembly compression info not found for key '__CompressedAssembliesInfo:D:\emcli
ent\MailClient.Mobile\MailClient.Mobile.Android\MailClient.Mobile.Android.csproj'. Compression will not be perform
ed.
at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.GetCompressedAssemblyInfo()
at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.RunTask()
at Microsoft.Android.Build.Tasks.AndroidTask.Execute()

Since NativeAOT doesn't operate with assemblies it seems safe to workaround it with -p:EmbedAssembliesIntoApk=false.

When doing multi-RID builds (again, default) I constantly got a variation of this error (for either RID at random):

 MailClient.Mobile.Android failed with 2 error(s) (35.1s)
ILLink : error IL1011: Failed to write 'obj\Release\net10.0-android\android-x64\linked\Java.Interop.dll'.
C:\Users\filip\.nuget\packages\microsoft.net.illink.tasks\10.0.0-rc.2.25427.104\build\Microsoft.NET.ILLink.targets(103,5): error NETSDK1144: Optimizing assemblies for size failed.

To get past that I used -r android-x64 and the app was built successfully.

The startup took well over a minute and the whole log was spammed with the following:

09-10 22:00:00.319 5559 5576 W monodroid-assembly: virtual bool BridgeProcessing::maybe_call_gc_user_peerable_add_managed_reference(JNIEnv *, jobject, jobject)
09-10 22:00:00.319 5559 5576 W monodroid-assembly: virtual bool BridgeProcessing::maybe_call_gc_user_peerable_add_managed_reference(JNIEnv *, jobject, jobject)
...
09-10 22:00:00.340 5559 5576 W monodroid-assembly: virtual bool BridgeProcessing::maybe_call_gc_user_peerable_clear_managed_references(JNIEnv *, jobject)
09-10 22:00:00.340 5559 5576 W monodroid-assembly: virtual bool BridgeProcessing::maybe_call_gc_user_peerable_clear_managed_references(JNIEnv *, jobject)

Presumably the slow startup was caused just by the logging but it needs to be verified. Experiments with the previous PR showed similar behavior due to incorrectly set gref_gc_threshold which caused excessive GC triggers, but this seems to be handled correctly in this PR.

@grendello
grendelloforce-pushed the dev/grendel/naot-runtime branch from 493cbd8 to 96113beCompareSeptember 11, 2025 09:17
@grendello

Copy link
Copy Markdown
ContributorAuthor

The NativeAOT test on Windows is failing with:

 (_CollectAssembliesToCompress target) -> C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: System.InvalidOperationException: Assembly compression info not found for key '__CompressedAssembliesInfo:C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj'. Compression will not be performed. [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.GetCompressedAssemblyInfo() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.RunTask() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Microsoft.Android.Build.Tasks.AndroidTask.Execute() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]

I don't see any new RegisterTaskObject() calls.

So, is the problem specific to Windows? Or maybe the test is doing multiple RIDs?

The problem was Debug vs Release. I tested only in the former and disabled only part of compressed assembly support. It should work now for Release too.

@filipnavara

filipnavara commented Sep 11, 2025

Copy link
Copy Markdown
Member

The GREF threshold initialization still seems to be wrong:

...
09-11 15:18:56.370 10555 10555 I NativeAotFromAndroid: APPLICATION Initialize END
09-11 15:18:56.372 10555 10555 D monodroid-gc: 83 outstanding GREFs. Performing a full GC!
09-11 15:18:56.376 10555 10617 D monodroid-gc: 84 outstanding GREFs. Performing a full GC!
09-11 15:18:56.392 10555 10555 D monodroid-gc: 59 outstanding GREFs. Performing a full GC!
09-11 15:18:56.400 10555 10617 D monodroid-gc: 60 outstanding GREFs. Performing a full GC!
09-11 15:18:56.416 10555 10573 I ient.mailclient: Explicit concurrent mark compact GC freed 1132KB AllocSpace bytes, 0(0B) LOS objects, 49% free, 2752KB/5504KB, paused 2.622ms,1.462ms total 35.554ms
09-11 15:18:56.419 10555 10555 D monodroid-gc: 86 outstanding GREFs. Performing a full GC!
09-11 15:18:56.419 10555 10617 D monodroid-gc: 86 outstanding GREFs. Performing a full GC!
09-11 15:18:56.448 10555 10573 I ient.mailclient: Explicit concurrent mark compact GC freed 152KB AllocSpace bytes, 0(0B) LOS objects, 49% free, 2728KB/5456KB, paused 1.917ms,2.356ms total 23.981ms
09-11 15:18:56.455 10555 10555 D monodroid-gc: 87 outstanding GREFs. Performing a full GC!
09-11 15:18:56.482 10555 10573 I ient.mailclient: Explicit concurrent mark compact GC freed 160KB AllocSpace bytes, 0(0B) LOS objects, 49% free, 2696KB/5392KB, paused 1.136ms,1.627ms total 21.812ms
09-11 15:18:56.488 10555 10555 D monodroid-gc: 88 outstanding GREFs. Performing a full GC!
09-11 15:18:56.515 10555 10573 I ient.mailclient: Explicit concurrent mark compact GC freed 96KB AllocSpace bytes, 0(0B) LOS objects, 49% free, 2696KB/5392KB, paused 730us,1.376ms total 22.020ms
09-11 15:18:56.517 10555 10555 D monodroid-gc: 89 outstanding GREFs. Performing a full GC!

Essentially every allocation of bridge object triggers a GC.

Probably a missing call to AndroidSystem::init_max_gref_count (); somewhere.

jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 11, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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, we've decided to introduce a new
stage to build and run these unit tests within an Android+NativeAOT
environment.
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.
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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 11, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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.
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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 12, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
Builds, installs, does nothing yet.
Linking seems to work, not tested yet
NativeAOT sample now links without errors about missing symbols (added
linker options for it to fail if missing symbols are detected)
@grendello
grendelloforce-pushed the dev/grendel/naot-runtime branch from 251297f to 68b9a88CompareSeptember 12, 2025 07:57
Comment on lines +105 to +106
// TODO: this shouldn't be public
public class AndroidObjectReferenceManager : JniRuntime.JniObjectReferenceManager {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the only thing we'll need to fix ASAP next week.

@grendello
grendello merged commit 869b0e0 into mainSep 12, 2025
59 checks passed
@grendello
grendello deleted the dev/grendel/naot-runtime branch September 12, 2025 14:14
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 15, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
grendello added a commit that referenced this pull request Sep 18, 2025
Context: #10461
A follow up to #10461 which adds some more compatibility with the "standard"
MonoVM and CoreCLR runtime hosts, namely:
* Support for setting environment variables via the environment file
(`<AndroidEnvironment>` MSBuild item)
* Support for setting **local** (that is, application level, not system
level) system properties
In addition to the above, it makes NativeAOT use static libc++ instead of the
shared one, which not only shrinks the package size (`libc++_shared.so` is
around 9mb), but it also makes startup slightly faster.
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 25, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 25, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 25, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 30, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 2, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 3, 2025
…droid
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 …`:
% 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.
Update `UnitTestsControl.cs` to provide better error messages when
tests fail in certain scenarios. Previously, many (many!) tests
would fail:
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
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
The 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…
Exclude tests which don't pass under NativeAOT. These fall into two
major categories:
1. Failures that don't make sense, failing due to the above two
mentioned exceptions.
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")]
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 3, 2025
…droid
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 …`:
% 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.
Update `UnitTestsControl.cs` to provide better error messages when
tests fail in certain scenarios. Previously, many (many!) tests
would fail:
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
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
The 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…
Exclude tests which don't pass under NativeAOT. These fall into two
major categories:
1. Failures that don't make sense, failing due to the above two
mentioned exceptions.
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")]
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 3, 2025
…droid
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 …`:
% 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.
Update `UnitTestsControl.cs` to provide better error messages when
tests fail in certain scenarios. Previously, many (many!) tests
would fail:
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
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
The 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…
Exclude tests which don't pass under NativeAOT. These fall into two
major categories:
1. Failures that don't make sense, failing due to the above two
mentioned exceptions.
These are ignored via:
[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")]
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 3, 2025
…droid
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 …`:
% 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.
Update `UnitTestsControl.cs` to provide better error messages when
tests fail in certain scenarios. Previously, many (many!) tests
would fail:
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
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
The 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…
Exclude tests which don't pass under NativeAOT. These fall into two
major categories:
1. Failures that don't make sense, failing due to the above two
mentioned exceptions.
These are ignored via:
[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")]
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 4, 2025
…droid
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 …`:
% 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.
Update `UnitTestsControl.cs` to provide better error messages when
tests fail in certain scenarios. Previously, many (many!) tests
would fail:
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
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
The 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…
Exclude tests which don't pass under NativeAOT. These fall into two
major categories:
1. Failures that don't make sense, failing due to the above two
mentioned exceptions.
These are ignored via:
[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")]
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 6, 2025
…droid
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 …`:
% 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 two
major categories:
1. Failures that don't make sense, failing due to
`TargetParameterCountException` or `InvalidOperationException`
described below.
These are ignored via:
[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")]
Update `UnitTestsControl.cs` to try to work around NativeAOT
"weirdness" and provide better error messages when
tests fail in certain scenarios. Previously, many (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
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.
Additionally, if a `TargetInvocationException` is thrown when the
the method arguments count and parameters count match, throw an
`AssertInconclusiveException`.
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.
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 6, 2025
…droid
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 …`:
% 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 two
major categories:
1. Failures that don't make sense, failing due to
`TargetParameterCountException` or `InvalidOperationException`
described below.
These are ignored via:
[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")]
Update `UnitTestsControl.cs` to try to work around NativeAOT
"weirdness" and provide better error messages when
tests fail in certain scenarios. Previously, many (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
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.
Additionally, if a `TargetInvocationException` is thrown when the
the method arguments count and parameters count match, throw an
`AssertInconclusiveException`.
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.
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 6, 2025
…droid
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 …`:
% 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
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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 7, 2025
…droid
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 …`:
% 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
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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 10, 2025
…droid
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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 10, 2025
…droid
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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 10, 2025
…droid
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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 10, 2025
…droid
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@90ac202
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Oct 13, 2025
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Create our initial native library for NativeAOT

5 participants

@grendello@jonpryor@jkoritzinsky@filipnavara@jonathanpeppers
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

[native] NativeAOT runtime - #10461

Merged
grendello merged 26 commits into
mainfrom
dev/grendel/naot-runtime
Sep 12, 2025
Merged

[native] NativeAOT runtime#10461
grendello merged 26 commits into
mainfrom
dev/grendel/naot-runtime

Conversation

@grendello

@grendellogrendello commented Sep 1, 2025

Copy link
Copy Markdown
Contributor

Fixes: #10455

Implements a minimal Android host for NativeAOT applications. The goal is to create
a runtime environment that is as compatible with Xamarin.Android / .NET for Android
applications using MonoVM or CoreCLR as possible. This PR implements the very basics:

  • Support for pre-loading of JNI libraries
  • Support for setting of the application environment variables (via <AndroidEnvironment>)
  • Support for the CoreCLR GC bridge

An important element that's currently missing is linking libc++ statically into the runtime, instead we include the shared libc++ from the NDK. The reason for this is that
NativeAOT build task references a compatibility C++ library which produces conflicts with
the static libc++ one when attempting to link. This issue will be fixed in a separate
PR.

@grendello
grendelloforce-pushed the dev/grendel/naot-runtime branch 2 times, most recently from 443bfb4 to 03d77e5CompareSeptember 2, 2025 14:15
@grendello

Copy link
Copy Markdown
ContributorAuthor

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@grendello
grendelloforce-pushed the dev/grendel/naot-runtime branch from 03d77e5 to 04ca2d9CompareSeptember 3, 2025 12:17
@jonpryor

Copy link
Copy Markdown
Contributor

Requested feature addition: CoreCLR has logic to use libSystem.Security.Cryptography.Native.Android.a":

It would be handy if similar integration could be done with NativeAOT, such that AndroidCryptoNative_InitLibraryOnLoad() is invoked during app startup when crypto support is required. This would remove the need for the kludge described here:

@grendello

Copy link
Copy Markdown
ContributorAuthor

@jonpryor I plan to add support for this, definitely. It'll be the same mechanism as with CoreCLR, just need to implement a bit more things before that

@jkoritzinsky

Copy link
Copy Markdown
Member

@grendello@jonathanpeppers should I close #10402 if this is intended to replace it?

@grendello

Copy link
Copy Markdown
ContributorAuthor

@jkoritzinsky no, please keep it open. I'm not going to put everything in this PR that's in #10402. Once this one is done, you can rebase yours and apply the remaining changes + finetuning + whatnot.

I should be done with this PR next week (need to add two more things)

@grendello
grendelloforce-pushed the dev/grendel/naot-runtime branch from bb04b1e to e6d636aCompareSeptember 9, 2025 13:17
@grendello
grendello marked this pull request as ready for review September 10, 2025 14:08

@jonathanpeppersjonathanpeppers left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The NativeAOT test on Windows is failing with:

 (_CollectAssembliesToCompress target) -> C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: System.InvalidOperationException: Assembly compression info not found for key '__CompressedAssembliesInfo:C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj'. Compression will not be performed. [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.GetCompressedAssemblyInfo() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.RunTask() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Microsoft.Android.Build.Tasks.AndroidTask.Execute() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]

I don't see any new RegisterTaskObject() calls.

So, is the problem specific to Windows? Or maybe the test is doing multiple RIDs?

@@ -110,9 +115,96 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android.
<ResolvedFileToPublish Include="$(_NdkSysrootDir)libc++_shared.so" RuntimeIdentifier="$(RuntimeIdentifier)" />

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be removed? Are we statically linking libc++?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're not right now because NativeAOT has a compatibility library that conflicts with static libc++. I want to explore a fix for this in a separate PR, since this one is too big already.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree it makes sense to split that off into separate PR. I suppose you can remove the shim libc++ from the list and then add the real one instead.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to keep both references to libc++_shared.so in this PR as we both need to link against it (the <LinkerArg> item) and package it (the ResolvedFileToPublish item)

Comment on lines +86 to +88
if (gref_gc_threshold != int.MaxValue) {
gref_gc_threshold = checked((gref_gc_threshold * 9) / 10);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this taking 90% of this number for all runtimes? Do we need this change?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other runtimes set gref_gc_threshold through different code path but it's still the same value (90% of the max GREF count).

@filipnavara

Copy link
Copy Markdown
Member

This mostly seems to work, except few build issues and excessive logging.

When doing dotnet publish the build configuration is automatically set to Release and that in turn sets EmbedAssembliesIntoApk=true, which later leads to this build error:

 D:\dotnet-android\android\bin\Debug\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009:
System.InvalidOperationException: Assembly compression info not found for key '__CompressedAssembliesInfo:D:\emcli
ent\MailClient.Mobile\MailClient.Mobile.Android\MailClient.Mobile.Android.csproj'. Compression will not be perform
ed.
at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.GetCompressedAssemblyInfo()
at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.RunTask()
at Microsoft.Android.Build.Tasks.AndroidTask.Execute()

Since NativeAOT doesn't operate with assemblies it seems safe to workaround it with -p:EmbedAssembliesIntoApk=false.

When doing multi-RID builds (again, default) I constantly got a variation of this error (for either RID at random):

 MailClient.Mobile.Android failed with 2 error(s) (35.1s)
ILLink : error IL1011: Failed to write 'obj\Release\net10.0-android\android-x64\linked\Java.Interop.dll'.
C:\Users\filip\.nuget\packages\microsoft.net.illink.tasks\10.0.0-rc.2.25427.104\build\Microsoft.NET.ILLink.targets(103,5): error NETSDK1144: Optimizing assemblies for size failed.

To get past that I used -r android-x64 and the app was built successfully.

The startup took well over a minute and the whole log was spammed with the following:

09-10 22:00:00.319 5559 5576 W monodroid-assembly: virtual bool BridgeProcessing::maybe_call_gc_user_peerable_add_managed_reference(JNIEnv *, jobject, jobject)
09-10 22:00:00.319 5559 5576 W monodroid-assembly: virtual bool BridgeProcessing::maybe_call_gc_user_peerable_add_managed_reference(JNIEnv *, jobject, jobject)
...
09-10 22:00:00.340 5559 5576 W monodroid-assembly: virtual bool BridgeProcessing::maybe_call_gc_user_peerable_clear_managed_references(JNIEnv *, jobject)
09-10 22:00:00.340 5559 5576 W monodroid-assembly: virtual bool BridgeProcessing::maybe_call_gc_user_peerable_clear_managed_references(JNIEnv *, jobject)

Presumably the slow startup was caused just by the logging but it needs to be verified. Experiments with the previous PR showed similar behavior due to incorrectly set gref_gc_threshold which caused excessive GC triggers, but this seems to be handled correctly in this PR.

@grendello
grendelloforce-pushed the dev/grendel/naot-runtime branch from 493cbd8 to 96113beCompareSeptember 11, 2025 09:17
@grendello

Copy link
Copy Markdown
ContributorAuthor

The NativeAOT test on Windows is failing with:

 (_CollectAssembliesToCompress target) -> C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: System.InvalidOperationException: Assembly compression info not found for key '__CompressedAssembliesInfo:C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj'. Compression will not be performed. [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.GetCompressedAssemblyInfo() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.RunTask() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Microsoft.Android.Build.Tasks.AndroidTask.Execute() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]

I don't see any new RegisterTaskObject() calls.

So, is the problem specific to Windows? Or maybe the test is doing multiple RIDs?

The problem was Debug vs Release. I tested only in the former and disabled only part of compressed assembly support. It should work now for Release too.

@filipnavara

filipnavara commented Sep 11, 2025

Copy link
Copy Markdown
Member

The GREF threshold initialization still seems to be wrong:

...
09-11 15:18:56.370 10555 10555 I NativeAotFromAndroid: APPLICATION Initialize END
09-11 15:18:56.372 10555 10555 D monodroid-gc: 83 outstanding GREFs. Performing a full GC!
09-11 15:18:56.376 10555 10617 D monodroid-gc: 84 outstanding GREFs. Performing a full GC!
09-11 15:18:56.392 10555 10555 D monodroid-gc: 59 outstanding GREFs. Performing a full GC!
09-11 15:18:56.400 10555 10617 D monodroid-gc: 60 outstanding GREFs. Performing a full GC!
09-11 15:18:56.416 10555 10573 I ient.mailclient: Explicit concurrent mark compact GC freed 1132KB AllocSpace bytes, 0(0B) LOS objects, 49% free, 2752KB/5504KB, paused 2.622ms,1.462ms total 35.554ms
09-11 15:18:56.419 10555 10555 D monodroid-gc: 86 outstanding GREFs. Performing a full GC!
09-11 15:18:56.419 10555 10617 D monodroid-gc: 86 outstanding GREFs. Performing a full GC!
09-11 15:18:56.448 10555 10573 I ient.mailclient: Explicit concurrent mark compact GC freed 152KB AllocSpace bytes, 0(0B) LOS objects, 49% free, 2728KB/5456KB, paused 1.917ms,2.356ms total 23.981ms
09-11 15:18:56.455 10555 10555 D monodroid-gc: 87 outstanding GREFs. Performing a full GC!
09-11 15:18:56.482 10555 10573 I ient.mailclient: Explicit concurrent mark compact GC freed 160KB AllocSpace bytes, 0(0B) LOS objects, 49% free, 2696KB/5392KB, paused 1.136ms,1.627ms total 21.812ms
09-11 15:18:56.488 10555 10555 D monodroid-gc: 88 outstanding GREFs. Performing a full GC!
09-11 15:18:56.515 10555 10573 I ient.mailclient: Explicit concurrent mark compact GC freed 96KB AllocSpace bytes, 0(0B) LOS objects, 49% free, 2696KB/5392KB, paused 730us,1.376ms total 22.020ms
09-11 15:18:56.517 10555 10555 D monodroid-gc: 89 outstanding GREFs. Performing a full GC!

Essentially every allocation of bridge object triggers a GC.

Probably a missing call to AndroidSystem::init_max_gref_count (); somewhere.

jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 11, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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, we've decided to introduce a new
stage to build and run these unit tests within an Android+NativeAOT
environment.
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.
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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 11, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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.
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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 12, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
Builds, installs, does nothing yet.
Linking seems to work, not tested yet
NativeAOT sample now links without errors about missing symbols (added
linker options for it to fail if missing symbols are detected)
@grendello
grendelloforce-pushed the dev/grendel/naot-runtime branch from 251297f to 68b9a88CompareSeptember 12, 2025 07:57
Comment on lines +105 to +106
// TODO: this shouldn't be public
public class AndroidObjectReferenceManager : JniRuntime.JniObjectReferenceManager {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the only thing we'll need to fix ASAP next week.

@grendello
grendello merged commit 869b0e0 into mainSep 12, 2025
59 checks passed
@grendello
grendello deleted the dev/grendel/naot-runtime branch September 12, 2025 14:14
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 15, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
grendello added a commit that referenced this pull request Sep 18, 2025
Context: #10461
A follow up to #10461 which adds some more compatibility with the "standard"
MonoVM and CoreCLR runtime hosts, namely:
* Support for setting environment variables via the environment file
(`<AndroidEnvironment>` MSBuild item)
* Support for setting **local** (that is, application level, not system
level) system properties
In addition to the above, it makes NativeAOT use static libc++ instead of the
shared one, which not only shrinks the package size (`libc++_shared.so` is
around 9mb), but it also makes startup slightly faster.
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 25, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 25, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 25, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 30, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 2, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 3, 2025
…droid
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 …`:
% 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.
Update `UnitTestsControl.cs` to provide better error messages when
tests fail in certain scenarios. Previously, many (many!) tests
would fail:
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
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
The 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…
Exclude tests which don't pass under NativeAOT. These fall into two
major categories:
1. Failures that don't make sense, failing due to the above two
mentioned exceptions.
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")]
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 3, 2025
…droid
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 …`:
% 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.
Update `UnitTestsControl.cs` to provide better error messages when
tests fail in certain scenarios. Previously, many (many!) tests
would fail:
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
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
The 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…
Exclude tests which don't pass under NativeAOT. These fall into two
major categories:
1. Failures that don't make sense, failing due to the above two
mentioned exceptions.
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")]
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 3, 2025
…droid
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 …`:
% 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.
Update `UnitTestsControl.cs` to provide better error messages when
tests fail in certain scenarios. Previously, many (many!) tests
would fail:
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
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
The 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…
Exclude tests which don't pass under NativeAOT. These fall into two
major categories:
1. Failures that don't make sense, failing due to the above two
mentioned exceptions.
These are ignored via:
[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")]
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 3, 2025
…droid
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 …`:
% 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.
Update `UnitTestsControl.cs` to provide better error messages when
tests fail in certain scenarios. Previously, many (many!) tests
would fail:
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
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
The 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…
Exclude tests which don't pass under NativeAOT. These fall into two
major categories:
1. Failures that don't make sense, failing due to the above two
mentioned exceptions.
These are ignored via:
[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")]
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 4, 2025
…droid
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 …`:
% 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.
Update `UnitTestsControl.cs` to provide better error messages when
tests fail in certain scenarios. Previously, many (many!) tests
would fail:
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
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
The 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…
Exclude tests which don't pass under NativeAOT. These fall into two
major categories:
1. Failures that don't make sense, failing due to the above two
mentioned exceptions.
These are ignored via:
[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")]
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 6, 2025
…droid
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 …`:
% 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 two
major categories:
1. Failures that don't make sense, failing due to
`TargetParameterCountException` or `InvalidOperationException`
described below.
These are ignored via:
[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")]
Update `UnitTestsControl.cs` to try to work around NativeAOT
"weirdness" and provide better error messages when
tests fail in certain scenarios. Previously, many (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
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.
Additionally, if a `TargetInvocationException` is thrown when the
the method arguments count and parameters count match, throw an
`AssertInconclusiveException`.
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.
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 6, 2025
…droid
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 …`:
% 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 two
major categories:
1. Failures that don't make sense, failing due to
`TargetParameterCountException` or `InvalidOperationException`
described below.
These are ignored via:
[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")]
Update `UnitTestsControl.cs` to try to work around NativeAOT
"weirdness" and provide better error messages when
tests fail in certain scenarios. Previously, many (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
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.
Additionally, if a `TargetInvocationException` is thrown when the
the method arguments count and parameters count match, throw an
`AssertInconclusiveException`.
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.
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 6, 2025
…droid
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 …`:
% 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
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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 7, 2025
…droid
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 …`:
% 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
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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 10, 2025
…droid
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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 10, 2025
…droid
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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 10, 2025
…droid
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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 10, 2025
…droid
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@90ac202
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Oct 13, 2025
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Create our initial native library for NativeAOT

5 participants

@grendello@jonpryor@jkoritzinsky@filipnavara@jonathanpeppers
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[native] NativeAOT runtime - #10461

Merged
grendello merged 26 commits into
mainfrom
dev/grendel/naot-runtime
Sep 12, 2025
Merged

[native] NativeAOT runtime#10461
grendello merged 26 commits into
mainfrom
dev/grendel/naot-runtime

Conversation

@grendello

@grendellogrendello commented Sep 1, 2025

Copy link
Copy Markdown
Contributor

Fixes: #10455

Implements a minimal Android host for NativeAOT applications. The goal is to create
a runtime environment that is as compatible with Xamarin.Android / .NET for Android
applications using MonoVM or CoreCLR as possible. This PR implements the very basics:

  • Support for pre-loading of JNI libraries
  • Support for setting of the application environment variables (via <AndroidEnvironment>)
  • Support for the CoreCLR GC bridge

An important element that's currently missing is linking libc++ statically into the runtime, instead we include the shared libc++ from the NDK. The reason for this is that
NativeAOT build task references a compatibility C++ library which produces conflicts with
the static libc++ one when attempting to link. This issue will be fixed in a separate
PR.

@grendello
grendelloforce-pushed the dev/grendel/naot-runtime branch 2 times, most recently from 443bfb4 to 03d77e5CompareSeptember 2, 2025 14:15
@grendello

Copy link
Copy Markdown
ContributorAuthor

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@grendello
grendelloforce-pushed the dev/grendel/naot-runtime branch from 03d77e5 to 04ca2d9CompareSeptember 3, 2025 12:17
@jonpryor

Copy link
Copy Markdown
Contributor

Requested feature addition: CoreCLR has logic to use libSystem.Security.Cryptography.Native.Android.a":

It would be handy if similar integration could be done with NativeAOT, such that AndroidCryptoNative_InitLibraryOnLoad() is invoked during app startup when crypto support is required. This would remove the need for the kludge described here:

@grendello

Copy link
Copy Markdown
ContributorAuthor

@jonpryor I plan to add support for this, definitely. It'll be the same mechanism as with CoreCLR, just need to implement a bit more things before that

@jkoritzinsky

Copy link
Copy Markdown
Member

@grendello@jonathanpeppers should I close #10402 if this is intended to replace it?

@grendello

Copy link
Copy Markdown
ContributorAuthor

@jkoritzinsky no, please keep it open. I'm not going to put everything in this PR that's in #10402. Once this one is done, you can rebase yours and apply the remaining changes + finetuning + whatnot.

I should be done with this PR next week (need to add two more things)

@grendello
grendelloforce-pushed the dev/grendel/naot-runtime branch from bb04b1e to e6d636aCompareSeptember 9, 2025 13:17
@grendello
grendello marked this pull request as ready for review September 10, 2025 14:08

@jonathanpeppersjonathanpeppers left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The NativeAOT test on Windows is failing with:

 (_CollectAssembliesToCompress target) -> C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: System.InvalidOperationException: Assembly compression info not found for key '__CompressedAssembliesInfo:C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj'. Compression will not be performed. [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.GetCompressedAssemblyInfo() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.RunTask() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Microsoft.Android.Build.Tasks.AndroidTask.Execute() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]

I don't see any new RegisterTaskObject() calls.

So, is the problem specific to Windows? Or maybe the test is doing multiple RIDs?

@@ -110,9 +115,96 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android.
<ResolvedFileToPublish Include="$(_NdkSysrootDir)libc++_shared.so" RuntimeIdentifier="$(RuntimeIdentifier)" />

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be removed? Are we statically linking libc++?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're not right now because NativeAOT has a compatibility library that conflicts with static libc++. I want to explore a fix for this in a separate PR, since this one is too big already.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree it makes sense to split that off into separate PR. I suppose you can remove the shim libc++ from the list and then add the real one instead.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to keep both references to libc++_shared.so in this PR as we both need to link against it (the <LinkerArg> item) and package it (the ResolvedFileToPublish item)

Comment on lines +86 to +88
if (gref_gc_threshold != int.MaxValue) {
gref_gc_threshold = checked((gref_gc_threshold * 9) / 10);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this taking 90% of this number for all runtimes? Do we need this change?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other runtimes set gref_gc_threshold through different code path but it's still the same value (90% of the max GREF count).

@filipnavara

Copy link
Copy Markdown
Member

This mostly seems to work, except few build issues and excessive logging.

When doing dotnet publish the build configuration is automatically set to Release and that in turn sets EmbedAssembliesIntoApk=true, which later leads to this build error:

 D:\dotnet-android\android\bin\Debug\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009:
System.InvalidOperationException: Assembly compression info not found for key '__CompressedAssembliesInfo:D:\emcli
ent\MailClient.Mobile\MailClient.Mobile.Android\MailClient.Mobile.Android.csproj'. Compression will not be perform
ed.
at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.GetCompressedAssemblyInfo()
at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.RunTask()
at Microsoft.Android.Build.Tasks.AndroidTask.Execute()

Since NativeAOT doesn't operate with assemblies it seems safe to workaround it with -p:EmbedAssembliesIntoApk=false.

When doing multi-RID builds (again, default) I constantly got a variation of this error (for either RID at random):

 MailClient.Mobile.Android failed with 2 error(s) (35.1s)
ILLink : error IL1011: Failed to write 'obj\Release\net10.0-android\android-x64\linked\Java.Interop.dll'.
C:\Users\filip\.nuget\packages\microsoft.net.illink.tasks\10.0.0-rc.2.25427.104\build\Microsoft.NET.ILLink.targets(103,5): error NETSDK1144: Optimizing assemblies for size failed.

To get past that I used -r android-x64 and the app was built successfully.

The startup took well over a minute and the whole log was spammed with the following:

09-10 22:00:00.319 5559 5576 W monodroid-assembly: virtual bool BridgeProcessing::maybe_call_gc_user_peerable_add_managed_reference(JNIEnv *, jobject, jobject)
09-10 22:00:00.319 5559 5576 W monodroid-assembly: virtual bool BridgeProcessing::maybe_call_gc_user_peerable_add_managed_reference(JNIEnv *, jobject, jobject)
...
09-10 22:00:00.340 5559 5576 W monodroid-assembly: virtual bool BridgeProcessing::maybe_call_gc_user_peerable_clear_managed_references(JNIEnv *, jobject)
09-10 22:00:00.340 5559 5576 W monodroid-assembly: virtual bool BridgeProcessing::maybe_call_gc_user_peerable_clear_managed_references(JNIEnv *, jobject)

Presumably the slow startup was caused just by the logging but it needs to be verified. Experiments with the previous PR showed similar behavior due to incorrectly set gref_gc_threshold which caused excessive GC triggers, but this seems to be handled correctly in this PR.

@grendello
grendelloforce-pushed the dev/grendel/naot-runtime branch from 493cbd8 to 96113beCompareSeptember 11, 2025 09:17
@grendello

Copy link
Copy Markdown
ContributorAuthor

The NativeAOT test on Windows is failing with:

 (_CollectAssembliesToCompress target) -> C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: System.InvalidOperationException: Assembly compression info not found for key '__CompressedAssembliesInfo:C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj'. Compression will not be performed. [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.GetCompressedAssemblyInfo() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.RunTask() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Microsoft.Android.Build.Tasks.AndroidTask.Execute() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]

I don't see any new RegisterTaskObject() calls.

So, is the problem specific to Windows? Or maybe the test is doing multiple RIDs?

The problem was Debug vs Release. I tested only in the former and disabled only part of compressed assembly support. It should work now for Release too.

@filipnavara

filipnavara commented Sep 11, 2025

Copy link
Copy Markdown
Member

The GREF threshold initialization still seems to be wrong:

...
09-11 15:18:56.370 10555 10555 I NativeAotFromAndroid: APPLICATION Initialize END
09-11 15:18:56.372 10555 10555 D monodroid-gc: 83 outstanding GREFs. Performing a full GC!
09-11 15:18:56.376 10555 10617 D monodroid-gc: 84 outstanding GREFs. Performing a full GC!
09-11 15:18:56.392 10555 10555 D monodroid-gc: 59 outstanding GREFs. Performing a full GC!
09-11 15:18:56.400 10555 10617 D monodroid-gc: 60 outstanding GREFs. Performing a full GC!
09-11 15:18:56.416 10555 10573 I ient.mailclient: Explicit concurrent mark compact GC freed 1132KB AllocSpace bytes, 0(0B) LOS objects, 49% free, 2752KB/5504KB, paused 2.622ms,1.462ms total 35.554ms
09-11 15:18:56.419 10555 10555 D monodroid-gc: 86 outstanding GREFs. Performing a full GC!
09-11 15:18:56.419 10555 10617 D monodroid-gc: 86 outstanding GREFs. Performing a full GC!
09-11 15:18:56.448 10555 10573 I ient.mailclient: Explicit concurrent mark compact GC freed 152KB AllocSpace bytes, 0(0B) LOS objects, 49% free, 2728KB/5456KB, paused 1.917ms,2.356ms total 23.981ms
09-11 15:18:56.455 10555 10555 D monodroid-gc: 87 outstanding GREFs. Performing a full GC!
09-11 15:18:56.482 10555 10573 I ient.mailclient: Explicit concurrent mark compact GC freed 160KB AllocSpace bytes, 0(0B) LOS objects, 49% free, 2696KB/5392KB, paused 1.136ms,1.627ms total 21.812ms
09-11 15:18:56.488 10555 10555 D monodroid-gc: 88 outstanding GREFs. Performing a full GC!
09-11 15:18:56.515 10555 10573 I ient.mailclient: Explicit concurrent mark compact GC freed 96KB AllocSpace bytes, 0(0B) LOS objects, 49% free, 2696KB/5392KB, paused 730us,1.376ms total 22.020ms
09-11 15:18:56.517 10555 10555 D monodroid-gc: 89 outstanding GREFs. Performing a full GC!

Essentially every allocation of bridge object triggers a GC.

Probably a missing call to AndroidSystem::init_max_gref_count (); somewhere.

jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 11, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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, we've decided to introduce a new
stage to build and run these unit tests within an Android+NativeAOT
environment.
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.
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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 11, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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.
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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 12, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
Builds, installs, does nothing yet.
Linking seems to work, not tested yet
NativeAOT sample now links without errors about missing symbols (added
linker options for it to fail if missing symbols are detected)
@grendello
grendelloforce-pushed the dev/grendel/naot-runtime branch from 251297f to 68b9a88CompareSeptember 12, 2025 07:57
Comment on lines +105 to +106
// TODO: this shouldn't be public
public class AndroidObjectReferenceManager : JniRuntime.JniObjectReferenceManager {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the only thing we'll need to fix ASAP next week.

@grendello
grendello merged commit 869b0e0 into mainSep 12, 2025
59 checks passed
@grendello
grendello deleted the dev/grendel/naot-runtime branch September 12, 2025 14:14
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 15, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
grendello added a commit that referenced this pull request Sep 18, 2025
Context: #10461
A follow up to #10461 which adds some more compatibility with the "standard"
MonoVM and CoreCLR runtime hosts, namely:
* Support for setting environment variables via the environment file
(`<AndroidEnvironment>` MSBuild item)
* Support for setting **local** (that is, application level, not system
level) system properties
In addition to the above, it makes NativeAOT use static libc++ instead of the
shared one, which not only shrinks the package size (`libc++_shared.so` is
around 9mb), but it also makes startup slightly faster.
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 25, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 25, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 25, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 30, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 2, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 3, 2025
…droid
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 …`:
% 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.
Update `UnitTestsControl.cs` to provide better error messages when
tests fail in certain scenarios. Previously, many (many!) tests
would fail:
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
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
The 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…
Exclude tests which don't pass under NativeAOT. These fall into two
major categories:
1. Failures that don't make sense, failing due to the above two
mentioned exceptions.
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")]
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 3, 2025
…droid
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 …`:
% 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.
Update `UnitTestsControl.cs` to provide better error messages when
tests fail in certain scenarios. Previously, many (many!) tests
would fail:
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
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
The 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…
Exclude tests which don't pass under NativeAOT. These fall into two
major categories:
1. Failures that don't make sense, failing due to the above two
mentioned exceptions.
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")]
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 3, 2025
…droid
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 …`:
% 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.
Update `UnitTestsControl.cs` to provide better error messages when
tests fail in certain scenarios. Previously, many (many!) tests
would fail:
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
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
The 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…
Exclude tests which don't pass under NativeAOT. These fall into two
major categories:
1. Failures that don't make sense, failing due to the above two
mentioned exceptions.
These are ignored via:
[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")]
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 3, 2025
…droid
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 …`:
% 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.
Update `UnitTestsControl.cs` to provide better error messages when
tests fail in certain scenarios. Previously, many (many!) tests
would fail:
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
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
The 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…
Exclude tests which don't pass under NativeAOT. These fall into two
major categories:
1. Failures that don't make sense, failing due to the above two
mentioned exceptions.
These are ignored via:
[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")]
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 4, 2025
…droid
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 …`:
% 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.
Update `UnitTestsControl.cs` to provide better error messages when
tests fail in certain scenarios. Previously, many (many!) tests
would fail:
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
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
The 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…
Exclude tests which don't pass under NativeAOT. These fall into two
major categories:
1. Failures that don't make sense, failing due to the above two
mentioned exceptions.
These are ignored via:
[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")]
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 6, 2025
…droid
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 …`:
% 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 two
major categories:
1. Failures that don't make sense, failing due to
`TargetParameterCountException` or `InvalidOperationException`
described below.
These are ignored via:
[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")]
Update `UnitTestsControl.cs` to try to work around NativeAOT
"weirdness" and provide better error messages when
tests fail in certain scenarios. Previously, many (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
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.
Additionally, if a `TargetInvocationException` is thrown when the
the method arguments count and parameters count match, throw an
`AssertInconclusiveException`.
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.
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 6, 2025
…droid
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 …`:
% 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 two
major categories:
1. Failures that don't make sense, failing due to
`TargetParameterCountException` or `InvalidOperationException`
described below.
These are ignored via:
[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")]
Update `UnitTestsControl.cs` to try to work around NativeAOT
"weirdness" and provide better error messages when
tests fail in certain scenarios. Previously, many (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
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.
Additionally, if a `TargetInvocationException` is thrown when the
the method arguments count and parameters count match, throw an
`AssertInconclusiveException`.
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.
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 6, 2025
…droid
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 …`:
% 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
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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 7, 2025
…droid
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 …`:
% 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
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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 10, 2025
…droid
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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 10, 2025
…droid
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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 10, 2025
…droid
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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 10, 2025
…droid
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@90ac202
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Oct 13, 2025
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Create our initial native library for NativeAOT

5 participants

@grendello@jonpryor@jkoritzinsky@filipnavara@jonathanpeppers
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[native] NativeAOT runtime - #10461

Merged
grendello merged 26 commits into
mainfrom
dev/grendel/naot-runtime
Sep 12, 2025
Merged

[native] NativeAOT runtime#10461
grendello merged 26 commits into
mainfrom
dev/grendel/naot-runtime

Conversation

@grendello

@grendellogrendello commented Sep 1, 2025

Copy link
Copy Markdown
Contributor

Fixes: #10455

Implements a minimal Android host for NativeAOT applications. The goal is to create
a runtime environment that is as compatible with Xamarin.Android / .NET for Android
applications using MonoVM or CoreCLR as possible. This PR implements the very basics:

  • Support for pre-loading of JNI libraries
  • Support for setting of the application environment variables (via <AndroidEnvironment>)
  • Support for the CoreCLR GC bridge

An important element that's currently missing is linking libc++ statically into the runtime, instead we include the shared libc++ from the NDK. The reason for this is that
NativeAOT build task references a compatibility C++ library which produces conflicts with
the static libc++ one when attempting to link. This issue will be fixed in a separate
PR.

@grendello
grendelloforce-pushed the dev/grendel/naot-runtime branch 2 times, most recently from 443bfb4 to 03d77e5CompareSeptember 2, 2025 14:15
@grendello

Copy link
Copy Markdown
ContributorAuthor

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@grendello
grendelloforce-pushed the dev/grendel/naot-runtime branch from 03d77e5 to 04ca2d9CompareSeptember 3, 2025 12:17
@jonpryor

Copy link
Copy Markdown
Contributor

Requested feature addition: CoreCLR has logic to use libSystem.Security.Cryptography.Native.Android.a":

It would be handy if similar integration could be done with NativeAOT, such that AndroidCryptoNative_InitLibraryOnLoad() is invoked during app startup when crypto support is required. This would remove the need for the kludge described here:

@grendello

Copy link
Copy Markdown
ContributorAuthor

@jonpryor I plan to add support for this, definitely. It'll be the same mechanism as with CoreCLR, just need to implement a bit more things before that

@jkoritzinsky

Copy link
Copy Markdown
Member

@grendello@jonathanpeppers should I close #10402 if this is intended to replace it?

@grendello

Copy link
Copy Markdown
ContributorAuthor

@jkoritzinsky no, please keep it open. I'm not going to put everything in this PR that's in #10402. Once this one is done, you can rebase yours and apply the remaining changes + finetuning + whatnot.

I should be done with this PR next week (need to add two more things)

@grendello
grendelloforce-pushed the dev/grendel/naot-runtime branch from bb04b1e to e6d636aCompareSeptember 9, 2025 13:17
@grendello
grendello marked this pull request as ready for review September 10, 2025 14:08

@jonathanpeppersjonathanpeppers left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The NativeAOT test on Windows is failing with:

 (_CollectAssembliesToCompress target) -> C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: System.InvalidOperationException: Assembly compression info not found for key '__CompressedAssembliesInfo:C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj'. Compression will not be performed. [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.GetCompressedAssemblyInfo() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.RunTask() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Microsoft.Android.Build.Tasks.AndroidTask.Execute() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]

I don't see any new RegisterTaskObject() calls.

So, is the problem specific to Windows? Or maybe the test is doing multiple RIDs?

@@ -110,9 +115,96 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android.
<ResolvedFileToPublish Include="$(_NdkSysrootDir)libc++_shared.so" RuntimeIdentifier="$(RuntimeIdentifier)" />

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be removed? Are we statically linking libc++?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're not right now because NativeAOT has a compatibility library that conflicts with static libc++. I want to explore a fix for this in a separate PR, since this one is too big already.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree it makes sense to split that off into separate PR. I suppose you can remove the shim libc++ from the list and then add the real one instead.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to keep both references to libc++_shared.so in this PR as we both need to link against it (the <LinkerArg> item) and package it (the ResolvedFileToPublish item)

Comment on lines +86 to +88
if (gref_gc_threshold != int.MaxValue) {
gref_gc_threshold = checked((gref_gc_threshold * 9) / 10);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this taking 90% of this number for all runtimes? Do we need this change?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other runtimes set gref_gc_threshold through different code path but it's still the same value (90% of the max GREF count).

@filipnavara

Copy link
Copy Markdown
Member

This mostly seems to work, except few build issues and excessive logging.

When doing dotnet publish the build configuration is automatically set to Release and that in turn sets EmbedAssembliesIntoApk=true, which later leads to this build error:

 D:\dotnet-android\android\bin\Debug\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009:
System.InvalidOperationException: Assembly compression info not found for key '__CompressedAssembliesInfo:D:\emcli
ent\MailClient.Mobile\MailClient.Mobile.Android\MailClient.Mobile.Android.csproj'. Compression will not be perform
ed.
at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.GetCompressedAssemblyInfo()
at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.RunTask()
at Microsoft.Android.Build.Tasks.AndroidTask.Execute()

Since NativeAOT doesn't operate with assemblies it seems safe to workaround it with -p:EmbedAssembliesIntoApk=false.

When doing multi-RID builds (again, default) I constantly got a variation of this error (for either RID at random):

 MailClient.Mobile.Android failed with 2 error(s) (35.1s)
ILLink : error IL1011: Failed to write 'obj\Release\net10.0-android\android-x64\linked\Java.Interop.dll'.
C:\Users\filip\.nuget\packages\microsoft.net.illink.tasks\10.0.0-rc.2.25427.104\build\Microsoft.NET.ILLink.targets(103,5): error NETSDK1144: Optimizing assemblies for size failed.

To get past that I used -r android-x64 and the app was built successfully.

The startup took well over a minute and the whole log was spammed with the following:

09-10 22:00:00.319 5559 5576 W monodroid-assembly: virtual bool BridgeProcessing::maybe_call_gc_user_peerable_add_managed_reference(JNIEnv *, jobject, jobject)
09-10 22:00:00.319 5559 5576 W monodroid-assembly: virtual bool BridgeProcessing::maybe_call_gc_user_peerable_add_managed_reference(JNIEnv *, jobject, jobject)
...
09-10 22:00:00.340 5559 5576 W monodroid-assembly: virtual bool BridgeProcessing::maybe_call_gc_user_peerable_clear_managed_references(JNIEnv *, jobject)
09-10 22:00:00.340 5559 5576 W monodroid-assembly: virtual bool BridgeProcessing::maybe_call_gc_user_peerable_clear_managed_references(JNIEnv *, jobject)

Presumably the slow startup was caused just by the logging but it needs to be verified. Experiments with the previous PR showed similar behavior due to incorrectly set gref_gc_threshold which caused excessive GC triggers, but this seems to be handled correctly in this PR.

@grendello
grendelloforce-pushed the dev/grendel/naot-runtime branch from 493cbd8 to 96113beCompareSeptember 11, 2025 09:17
@grendello

Copy link
Copy Markdown
ContributorAuthor

The NativeAOT test on Windows is failing with:

 (_CollectAssembliesToCompress target) -> C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: System.InvalidOperationException: Assembly compression info not found for key '__CompressedAssembliesInfo:C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj'. Compression will not be performed. [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.GetCompressedAssemblyInfo() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.RunTask() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Microsoft.Android.Build.Tasks.AndroidTask.Execute() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]

I don't see any new RegisterTaskObject() calls.

So, is the problem specific to Windows? Or maybe the test is doing multiple RIDs?

The problem was Debug vs Release. I tested only in the former and disabled only part of compressed assembly support. It should work now for Release too.

@filipnavara

filipnavara commented Sep 11, 2025

Copy link
Copy Markdown
Member

The GREF threshold initialization still seems to be wrong:

...
09-11 15:18:56.370 10555 10555 I NativeAotFromAndroid: APPLICATION Initialize END
09-11 15:18:56.372 10555 10555 D monodroid-gc: 83 outstanding GREFs. Performing a full GC!
09-11 15:18:56.376 10555 10617 D monodroid-gc: 84 outstanding GREFs. Performing a full GC!
09-11 15:18:56.392 10555 10555 D monodroid-gc: 59 outstanding GREFs. Performing a full GC!
09-11 15:18:56.400 10555 10617 D monodroid-gc: 60 outstanding GREFs. Performing a full GC!
09-11 15:18:56.416 10555 10573 I ient.mailclient: Explicit concurrent mark compact GC freed 1132KB AllocSpace bytes, 0(0B) LOS objects, 49% free, 2752KB/5504KB, paused 2.622ms,1.462ms total 35.554ms
09-11 15:18:56.419 10555 10555 D monodroid-gc: 86 outstanding GREFs. Performing a full GC!
09-11 15:18:56.419 10555 10617 D monodroid-gc: 86 outstanding GREFs. Performing a full GC!
09-11 15:18:56.448 10555 10573 I ient.mailclient: Explicit concurrent mark compact GC freed 152KB AllocSpace bytes, 0(0B) LOS objects, 49% free, 2728KB/5456KB, paused 1.917ms,2.356ms total 23.981ms
09-11 15:18:56.455 10555 10555 D monodroid-gc: 87 outstanding GREFs. Performing a full GC!
09-11 15:18:56.482 10555 10573 I ient.mailclient: Explicit concurrent mark compact GC freed 160KB AllocSpace bytes, 0(0B) LOS objects, 49% free, 2696KB/5392KB, paused 1.136ms,1.627ms total 21.812ms
09-11 15:18:56.488 10555 10555 D monodroid-gc: 88 outstanding GREFs. Performing a full GC!
09-11 15:18:56.515 10555 10573 I ient.mailclient: Explicit concurrent mark compact GC freed 96KB AllocSpace bytes, 0(0B) LOS objects, 49% free, 2696KB/5392KB, paused 730us,1.376ms total 22.020ms
09-11 15:18:56.517 10555 10555 D monodroid-gc: 89 outstanding GREFs. Performing a full GC!

Essentially every allocation of bridge object triggers a GC.

Probably a missing call to AndroidSystem::init_max_gref_count (); somewhere.

jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 11, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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, we've decided to introduce a new
stage to build and run these unit tests within an Android+NativeAOT
environment.
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.
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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 11, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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.
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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 12, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
Builds, installs, does nothing yet.
Linking seems to work, not tested yet
NativeAOT sample now links without errors about missing symbols (added
linker options for it to fail if missing symbols are detected)
@grendello
grendelloforce-pushed the dev/grendel/naot-runtime branch from 251297f to 68b9a88CompareSeptember 12, 2025 07:57
Comment on lines +105 to +106
// TODO: this shouldn't be public
public class AndroidObjectReferenceManager : JniRuntime.JniObjectReferenceManager {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the only thing we'll need to fix ASAP next week.

@grendello
grendello merged commit 869b0e0 into mainSep 12, 2025
59 checks passed
@grendello
grendello deleted the dev/grendel/naot-runtime branch September 12, 2025 14:14
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 15, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
grendello added a commit that referenced this pull request Sep 18, 2025
Context: #10461
A follow up to #10461 which adds some more compatibility with the "standard"
MonoVM and CoreCLR runtime hosts, namely:
* Support for setting environment variables via the environment file
(`<AndroidEnvironment>` MSBuild item)
* Support for setting **local** (that is, application level, not system
level) system properties
In addition to the above, it makes NativeAOT use static libc++ instead of the
shared one, which not only shrinks the package size (`libc++_shared.so` is
around 9mb), but it also makes startup slightly faster.
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 25, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 25, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 25, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 30, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 2, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 3, 2025
…droid
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 …`:
% 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.
Update `UnitTestsControl.cs` to provide better error messages when
tests fail in certain scenarios. Previously, many (many!) tests
would fail:
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
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
The 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…
Exclude tests which don't pass under NativeAOT. These fall into two
major categories:
1. Failures that don't make sense, failing due to the above two
mentioned exceptions.
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")]
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 3, 2025
…droid
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 …`:
% 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.
Update `UnitTestsControl.cs` to provide better error messages when
tests fail in certain scenarios. Previously, many (many!) tests
would fail:
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
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
The 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…
Exclude tests which don't pass under NativeAOT. These fall into two
major categories:
1. Failures that don't make sense, failing due to the above two
mentioned exceptions.
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")]
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 3, 2025
…droid
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 …`:
% 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.
Update `UnitTestsControl.cs` to provide better error messages when
tests fail in certain scenarios. Previously, many (many!) tests
would fail:
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
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
The 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…
Exclude tests which don't pass under NativeAOT. These fall into two
major categories:
1. Failures that don't make sense, failing due to the above two
mentioned exceptions.
These are ignored via:
[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")]
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 3, 2025
…droid
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 …`:
% 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.
Update `UnitTestsControl.cs` to provide better error messages when
tests fail in certain scenarios. Previously, many (many!) tests
would fail:
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
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
The 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…
Exclude tests which don't pass under NativeAOT. These fall into two
major categories:
1. Failures that don't make sense, failing due to the above two
mentioned exceptions.
These are ignored via:
[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")]
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 4, 2025
…droid
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 …`:
% 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.
Update `UnitTestsControl.cs` to provide better error messages when
tests fail in certain scenarios. Previously, many (many!) tests
would fail:
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
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
The 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…
Exclude tests which don't pass under NativeAOT. These fall into two
major categories:
1. Failures that don't make sense, failing due to the above two
mentioned exceptions.
These are ignored via:
[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")]
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 6, 2025
…droid
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 …`:
% 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 two
major categories:
1. Failures that don't make sense, failing due to
`TargetParameterCountException` or `InvalidOperationException`
described below.
These are ignored via:
[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")]
Update `UnitTestsControl.cs` to try to work around NativeAOT
"weirdness" and provide better error messages when
tests fail in certain scenarios. Previously, many (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
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.
Additionally, if a `TargetInvocationException` is thrown when the
the method arguments count and parameters count match, throw an
`AssertInconclusiveException`.
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.
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 6, 2025
…droid
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 …`:
% 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 two
major categories:
1. Failures that don't make sense, failing due to
`TargetParameterCountException` or `InvalidOperationException`
described below.
These are ignored via:
[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")]
Update `UnitTestsControl.cs` to try to work around NativeAOT
"weirdness" and provide better error messages when
tests fail in certain scenarios. Previously, many (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
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.
Additionally, if a `TargetInvocationException` is thrown when the
the method arguments count and parameters count match, throw an
`AssertInconclusiveException`.
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.
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 6, 2025
…droid
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 …`:
% 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
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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 7, 2025
…droid
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 …`:
% 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
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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 10, 2025
…droid
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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 10, 2025
…droid
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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 10, 2025
…droid
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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 10, 2025
…droid
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@90ac202
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Oct 13, 2025
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Create our initial native library for NativeAOT

5 participants

@grendello@jonpryor@jkoritzinsky@filipnavara@jonathanpeppers
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

[native] NativeAOT runtime - #10461

Merged
grendello merged 26 commits into
mainfrom
dev/grendel/naot-runtime
Sep 12, 2025
Merged

[native] NativeAOT runtime#10461
grendello merged 26 commits into
mainfrom
dev/grendel/naot-runtime

Conversation

@grendello

@grendellogrendello commented Sep 1, 2025

Copy link
Copy Markdown
Contributor

Fixes: #10455

Implements a minimal Android host for NativeAOT applications. The goal is to create
a runtime environment that is as compatible with Xamarin.Android / .NET for Android
applications using MonoVM or CoreCLR as possible. This PR implements the very basics:

  • Support for pre-loading of JNI libraries
  • Support for setting of the application environment variables (via <AndroidEnvironment>)
  • Support for the CoreCLR GC bridge

An important element that's currently missing is linking libc++ statically into the runtime, instead we include the shared libc++ from the NDK. The reason for this is that
NativeAOT build task references a compatibility C++ library which produces conflicts with
the static libc++ one when attempting to link. This issue will be fixed in a separate
PR.

@grendello
grendelloforce-pushed the dev/grendel/naot-runtime branch 2 times, most recently from 443bfb4 to 03d77e5CompareSeptember 2, 2025 14:15
@grendello

Copy link
Copy Markdown
ContributorAuthor

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@grendello
grendelloforce-pushed the dev/grendel/naot-runtime branch from 03d77e5 to 04ca2d9CompareSeptember 3, 2025 12:17
@jonpryor

Copy link
Copy Markdown
Contributor

Requested feature addition: CoreCLR has logic to use libSystem.Security.Cryptography.Native.Android.a":

It would be handy if similar integration could be done with NativeAOT, such that AndroidCryptoNative_InitLibraryOnLoad() is invoked during app startup when crypto support is required. This would remove the need for the kludge described here:

@grendello

Copy link
Copy Markdown
ContributorAuthor

@jonpryor I plan to add support for this, definitely. It'll be the same mechanism as with CoreCLR, just need to implement a bit more things before that

@jkoritzinsky

Copy link
Copy Markdown
Member

@grendello@jonathanpeppers should I close #10402 if this is intended to replace it?

@grendello

Copy link
Copy Markdown
ContributorAuthor

@jkoritzinsky no, please keep it open. I'm not going to put everything in this PR that's in #10402. Once this one is done, you can rebase yours and apply the remaining changes + finetuning + whatnot.

I should be done with this PR next week (need to add two more things)

@grendello
grendelloforce-pushed the dev/grendel/naot-runtime branch from bb04b1e to e6d636aCompareSeptember 9, 2025 13:17
@grendello
grendello marked this pull request as ready for review September 10, 2025 14:08

@jonathanpeppersjonathanpeppers left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The NativeAOT test on Windows is failing with:

 (_CollectAssembliesToCompress target) -> C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: System.InvalidOperationException: Assembly compression info not found for key '__CompressedAssembliesInfo:C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj'. Compression will not be performed. [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.GetCompressedAssemblyInfo() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.RunTask() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Microsoft.Android.Build.Tasks.AndroidTask.Execute() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]

I don't see any new RegisterTaskObject() calls.

So, is the problem specific to Windows? Or maybe the test is doing multiple RIDs?

@@ -110,9 +115,96 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android.
<ResolvedFileToPublish Include="$(_NdkSysrootDir)libc++_shared.so" RuntimeIdentifier="$(RuntimeIdentifier)" />

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be removed? Are we statically linking libc++?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're not right now because NativeAOT has a compatibility library that conflicts with static libc++. I want to explore a fix for this in a separate PR, since this one is too big already.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree it makes sense to split that off into separate PR. I suppose you can remove the shim libc++ from the list and then add the real one instead.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to keep both references to libc++_shared.so in this PR as we both need to link against it (the <LinkerArg> item) and package it (the ResolvedFileToPublish item)

Comment on lines +86 to +88
if (gref_gc_threshold != int.MaxValue) {
gref_gc_threshold = checked((gref_gc_threshold * 9) / 10);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this taking 90% of this number for all runtimes? Do we need this change?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other runtimes set gref_gc_threshold through different code path but it's still the same value (90% of the max GREF count).

@filipnavara

Copy link
Copy Markdown
Member

This mostly seems to work, except few build issues and excessive logging.

When doing dotnet publish the build configuration is automatically set to Release and that in turn sets EmbedAssembliesIntoApk=true, which later leads to this build error:

 D:\dotnet-android\android\bin\Debug\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009:
System.InvalidOperationException: Assembly compression info not found for key '__CompressedAssembliesInfo:D:\emcli
ent\MailClient.Mobile\MailClient.Mobile.Android\MailClient.Mobile.Android.csproj'. Compression will not be perform
ed.
at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.GetCompressedAssemblyInfo()
at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.RunTask()
at Microsoft.Android.Build.Tasks.AndroidTask.Execute()

Since NativeAOT doesn't operate with assemblies it seems safe to workaround it with -p:EmbedAssembliesIntoApk=false.

When doing multi-RID builds (again, default) I constantly got a variation of this error (for either RID at random):

 MailClient.Mobile.Android failed with 2 error(s) (35.1s)
ILLink : error IL1011: Failed to write 'obj\Release\net10.0-android\android-x64\linked\Java.Interop.dll'.
C:\Users\filip\.nuget\packages\microsoft.net.illink.tasks\10.0.0-rc.2.25427.104\build\Microsoft.NET.ILLink.targets(103,5): error NETSDK1144: Optimizing assemblies for size failed.

To get past that I used -r android-x64 and the app was built successfully.

The startup took well over a minute and the whole log was spammed with the following:

09-10 22:00:00.319 5559 5576 W monodroid-assembly: virtual bool BridgeProcessing::maybe_call_gc_user_peerable_add_managed_reference(JNIEnv *, jobject, jobject)
09-10 22:00:00.319 5559 5576 W monodroid-assembly: virtual bool BridgeProcessing::maybe_call_gc_user_peerable_add_managed_reference(JNIEnv *, jobject, jobject)
...
09-10 22:00:00.340 5559 5576 W monodroid-assembly: virtual bool BridgeProcessing::maybe_call_gc_user_peerable_clear_managed_references(JNIEnv *, jobject)
09-10 22:00:00.340 5559 5576 W monodroid-assembly: virtual bool BridgeProcessing::maybe_call_gc_user_peerable_clear_managed_references(JNIEnv *, jobject)

Presumably the slow startup was caused just by the logging but it needs to be verified. Experiments with the previous PR showed similar behavior due to incorrectly set gref_gc_threshold which caused excessive GC triggers, but this seems to be handled correctly in this PR.

@grendello
grendelloforce-pushed the dev/grendel/naot-runtime branch from 493cbd8 to 96113beCompareSeptember 11, 2025 09:17
@grendello

Copy link
Copy Markdown
ContributorAuthor

The NativeAOT test on Windows is failing with:

 (_CollectAssembliesToCompress target) -> C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: System.InvalidOperationException: Assembly compression info not found for key '__CompressedAssembliesInfo:C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj'. Compression will not be performed. [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.GetCompressedAssemblyInfo() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Xamarin.Android.Tasks.CollectAssemblyFilesToCompress.RunTask() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]
C:\a\_work\1\s\bin\Release\dotnet\packs\Microsoft.Android.Sdk.Windows\36.0.0-ci.dev-grendel-naot-runtime.323\tools\Xamarin.Android.Common.targets(2258,3): error XACAF7009: at Microsoft.Android.Build.Tasks.AndroidTask.Execute() [C:\a\_work\1\a\TestRelease\09-10_14.27.54\temp\NativeAOT\Hello.csproj]

I don't see any new RegisterTaskObject() calls.

So, is the problem specific to Windows? Or maybe the test is doing multiple RIDs?

The problem was Debug vs Release. I tested only in the former and disabled only part of compressed assembly support. It should work now for Release too.

@filipnavara

filipnavara commented Sep 11, 2025

Copy link
Copy Markdown
Member

The GREF threshold initialization still seems to be wrong:

...
09-11 15:18:56.370 10555 10555 I NativeAotFromAndroid: APPLICATION Initialize END
09-11 15:18:56.372 10555 10555 D monodroid-gc: 83 outstanding GREFs. Performing a full GC!
09-11 15:18:56.376 10555 10617 D monodroid-gc: 84 outstanding GREFs. Performing a full GC!
09-11 15:18:56.392 10555 10555 D monodroid-gc: 59 outstanding GREFs. Performing a full GC!
09-11 15:18:56.400 10555 10617 D monodroid-gc: 60 outstanding GREFs. Performing a full GC!
09-11 15:18:56.416 10555 10573 I ient.mailclient: Explicit concurrent mark compact GC freed 1132KB AllocSpace bytes, 0(0B) LOS objects, 49% free, 2752KB/5504KB, paused 2.622ms,1.462ms total 35.554ms
09-11 15:18:56.419 10555 10555 D monodroid-gc: 86 outstanding GREFs. Performing a full GC!
09-11 15:18:56.419 10555 10617 D monodroid-gc: 86 outstanding GREFs. Performing a full GC!
09-11 15:18:56.448 10555 10573 I ient.mailclient: Explicit concurrent mark compact GC freed 152KB AllocSpace bytes, 0(0B) LOS objects, 49% free, 2728KB/5456KB, paused 1.917ms,2.356ms total 23.981ms
09-11 15:18:56.455 10555 10555 D monodroid-gc: 87 outstanding GREFs. Performing a full GC!
09-11 15:18:56.482 10555 10573 I ient.mailclient: Explicit concurrent mark compact GC freed 160KB AllocSpace bytes, 0(0B) LOS objects, 49% free, 2696KB/5392KB, paused 1.136ms,1.627ms total 21.812ms
09-11 15:18:56.488 10555 10555 D monodroid-gc: 88 outstanding GREFs. Performing a full GC!
09-11 15:18:56.515 10555 10573 I ient.mailclient: Explicit concurrent mark compact GC freed 96KB AllocSpace bytes, 0(0B) LOS objects, 49% free, 2696KB/5392KB, paused 730us,1.376ms total 22.020ms
09-11 15:18:56.517 10555 10555 D monodroid-gc: 89 outstanding GREFs. Performing a full GC!

Essentially every allocation of bridge object triggers a GC.

Probably a missing call to AndroidSystem::init_max_gref_count (); somewhere.

jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 11, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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, we've decided to introduce a new
stage to build and run these unit tests within an Android+NativeAOT
environment.
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.
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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 11, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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.
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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 12, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
Builds, installs, does nothing yet.
Linking seems to work, not tested yet
NativeAOT sample now links without errors about missing symbols (added
linker options for it to fail if missing symbols are detected)
@grendello
grendelloforce-pushed the dev/grendel/naot-runtime branch from 251297f to 68b9a88CompareSeptember 12, 2025 07:57
Comment on lines +105 to +106
// TODO: this shouldn't be public
public class AndroidObjectReferenceManager : JniRuntime.JniObjectReferenceManager {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the only thing we'll need to fix ASAP next week.

@grendello
grendello merged commit 869b0e0 into mainSep 12, 2025
59 checks passed
@grendello
grendello deleted the dev/grendel/naot-runtime branch September 12, 2025 14:14
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 15, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
grendello added a commit that referenced this pull request Sep 18, 2025
Context: #10461
A follow up to #10461 which adds some more compatibility with the "standard"
MonoVM and CoreCLR runtime hosts, namely:
* Support for setting environment variables via the environment file
(`<AndroidEnvironment>` MSBuild item)
* Support for setting **local** (that is, application level, not system
level) system properties
In addition to the above, it makes NativeAOT use static libc++ instead of the
shared one, which not only shrinks the package size (`libc++_shared.so` is
around 9mb), but it also makes startup slightly faster.
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 25, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 25, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 25, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Sep 30, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 2, 2025
…droid
Context: #21256 / 52a6f3e
Context: dotnet/android#10461
Context: dotnet/android#10457
Context: dotnet/android#10463#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 …`:
% 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.
Finally, .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.
Note: Do *not* make Release-config apps [Debuggable][2] under
.NET 10 Preview 7 or earlier; you will run into
[dotnet/java-interop@90ac202e][3].
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 3, 2025
…droid
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 …`:
% 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.
Update `UnitTestsControl.cs` to provide better error messages when
tests fail in certain scenarios. Previously, many (many!) tests
would fail:
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
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
The 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…
Exclude tests which don't pass under NativeAOT. These fall into two
major categories:
1. Failures that don't make sense, failing due to the above two
mentioned exceptions.
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")]
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 3, 2025
…droid
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 …`:
% 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.
Update `UnitTestsControl.cs` to provide better error messages when
tests fail in certain scenarios. Previously, many (many!) tests
would fail:
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
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
The 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…
Exclude tests which don't pass under NativeAOT. These fall into two
major categories:
1. Failures that don't make sense, failing due to the above two
mentioned exceptions.
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")]
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 3, 2025
…droid
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 …`:
% 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.
Update `UnitTestsControl.cs` to provide better error messages when
tests fail in certain scenarios. Previously, many (many!) tests
would fail:
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
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
The 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…
Exclude tests which don't pass under NativeAOT. These fall into two
major categories:
1. Failures that don't make sense, failing due to the above two
mentioned exceptions.
These are ignored via:
[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")]
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 3, 2025
…droid
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 …`:
% 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.
Update `UnitTestsControl.cs` to provide better error messages when
tests fail in certain scenarios. Previously, many (many!) tests
would fail:
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
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
The 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…
Exclude tests which don't pass under NativeAOT. These fall into two
major categories:
1. Failures that don't make sense, failing due to the above two
mentioned exceptions.
These are ignored via:
[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")]
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 4, 2025
…droid
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 …`:
% 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.
Update `UnitTestsControl.cs` to provide better error messages when
tests fail in certain scenarios. Previously, many (many!) tests
would fail:
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
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
The 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…
Exclude tests which don't pass under NativeAOT. These fall into two
major categories:
1. Failures that don't make sense, failing due to the above two
mentioned exceptions.
These are ignored via:
[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")]
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 6, 2025
…droid
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 …`:
% 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 two
major categories:
1. Failures that don't make sense, failing due to
`TargetParameterCountException` or `InvalidOperationException`
described below.
These are ignored via:
[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")]
Update `UnitTestsControl.cs` to try to work around NativeAOT
"weirdness" and provide better error messages when
tests fail in certain scenarios. Previously, many (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
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.
Additionally, if a `TargetInvocationException` is thrown when the
the method arguments count and parameters count match, throw an
`AssertInconclusiveException`.
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.
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 6, 2025
…droid
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 …`:
% 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 two
major categories:
1. Failures that don't make sense, failing due to
`TargetParameterCountException` or `InvalidOperationException`
described below.
These are ignored via:
[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")]
Update `UnitTestsControl.cs` to try to work around NativeAOT
"weirdness" and provide better error messages when
tests fail in certain scenarios. Previously, many (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
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.
Additionally, if a `TargetInvocationException` is thrown when the
the method arguments count and parameters count match, throw an
`AssertInconclusiveException`.
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.
[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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 6, 2025
…droid
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 …`:
% 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
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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 7, 2025
…droid
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 …`:
% 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
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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 10, 2025
…droid
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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 10, 2025
…droid
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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 10, 2025
…droid
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@90ac202
jonpryor added a commit to unoplatform/uno that referenced this pull request Oct 10, 2025
…droid
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@90ac202
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Oct 13, 2025
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Create our initial native library for NativeAOT

5 participants

@grendello@jonpryor@jkoritzinsky@filipnavara@jonathanpeppers