Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion build-tools/create-packs/Microsoft.Android.Runtime.proj
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ projects that use the Microsoft.Android.Runtimes framework in .NET 6+.
DependsOnTargets="_GetLicense;_GetDefaultPackageVersion"
BeforeTargets="GetFilesToPackage" >
<PropertyGroup>
<_RuntimeFlavorDirName Condition=" '$(AndroidRuntime)' == 'CoreCLR' ">$(_CLRRuntimeFlavorDirName)</_RuntimeFlavorDirName>
<_RuntimeFlavorDirName Condition=" '$(AndroidRuntime)' == 'CoreCLR' or '$(AndroidRuntime)' == 'NativeAOT' ">$(_CLRRuntimeFlavorDirName)</_RuntimeFlavorDirName>
<_RuntimeFlavorDirName Condition=" '$(AndroidRuntime)' == 'Mono' Or '$(AndroidRuntime)' == '' ">$(_MonoRuntimeFlavorDirName)</_RuntimeFlavorDirName>
<_ClangArch Condition=" '$(AndroidRID)' == 'android-arm64' ">aarch64</_ClangArch>
<_ClangArch Condition=" '$(AndroidRID)' == 'android-arm' ">arm</_ClangArch>
Expand Down Expand Up @@ -103,6 +103,10 @@ projects that use the Microsoft.Android.Runtimes framework in .NET 6+.

<ItemGroup Condition=" '$(AndroidRuntime)' == 'NativeAOT' ">
<_AndroidRuntimePackAssemblies Include="$(_MonoAndroidNETOutputRoot)$(AndroidLatestStableApiLevel)\Microsoft.Android.Runtime.NativeAOT.dll" />
<NativeRuntimeAsset Condition=" Exists('$(NativeRuntimeOutputRootDir)$(_RuntimeFlavorDirName)\$(AndroidRID)\libxa-shared-bits-debug.a') " Include="$(NativeRuntimeOutputRootDir)$(_RuntimeFlavorDirName)\$(AndroidRID)\libxa-shared-bits-debug.a" />
<NativeRuntimeAsset Condition=" Exists('$(NativeRuntimeOutputRootDir)$(_RuntimeFlavorDirName)\$(AndroidRID)\libxa-shared-bits-release.a') " Include="$(NativeRuntimeOutputRootDir)$(_RuntimeFlavorDirName)\$(AndroidRID)\libxa-shared-bits-release.a" />
<NativeRuntimeAsset Condition=" Exists('$(NativeRuntimeOutputRootDir)$(_RuntimeFlavorDirName)\$(AndroidRID)\libnet-android-nativeaot-debug.a') " Include="$(NativeRuntimeOutputRootDir)$(_RuntimeFlavorDirName)\$(AndroidRID)\libnet-android-nativeaot-debug.a" />
<NativeRuntimeAsset Condition=" Exists('$(NativeRuntimeOutputRootDir)$(_RuntimeFlavorDirName)\$(AndroidRID)\libnet-android-nativeaot-release.a') " Include="$(NativeRuntimeOutputRootDir)$(_RuntimeFlavorDirName)\$(AndroidRID)\libnet-android-nativeaot-release.a" />
Comment on lines +106 to +109
Copy link
Member

Choose a reason for hiding this comment

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

Ok, I was building this locally to find out what was missing, get the same error as CI:

D:\.nuget\packages\microsoft.dotnet.ilcompiler\10.0.0-rc.2.25416.105\build\Microsoft.NETCore.Native.Publish.targets(73,5): error MSB4018:
      The "ComputeManagedAssembliesToCompileToNative" task failed unexpectedly.
      System.IO.FileNotFoundException: Could not find file 'D:\src\xamarin-android\bin\Debug\lib\packs\Microsoft.Android.Runtime.NativeAOT.36.android-arm64\36.0.0\runtimes\android-arm64\native\libnet-android-na
      tiveaot-debug.a'.
      File name: 'D:\src\xamarin-android\bin\Debug\lib\packs\Microsoft.Android.Runtime.NativeAOT.36.android-arm64\36.0.0\runtimes\android-arm64\native\libnet-android-nativeaot-debug.a'
         at System.IO.Strategies.OSFileStreamStrategy..ctor(String path, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize, Nullable`1 unixCreateMode)
         at System.IO.Strategies.FileStreamHelpers.ChooseStrategyCore(String path, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize, Nullable`1 unixCreateMode)
         at System.IO.File.OpenRead(String path)
         at Build.Tasks.ComputeManagedAssembliesToCompileToNative.Execute()
         at Microsoft.Build.BackEnd.TaskExecutionHost.Execute()
         at Microsoft.Build.BackEnd.TaskBuilder.ExecuteInstantiatedTask(TaskExecutionHost taskExecutionHost, TaskLoggingContext taskLoggingContext, TaskHost taskHost, ItemBucket bucket, TaskExecutionMode howToE
      xecuteTask)
  helloandroid failed with 1 error(s) (0.1s)

The file it's looking for doesn't exist.

And, I think the issue is all the Exists() check above are false.

Looking at src\native\native.targets, I don't think anything copies libnet-android-nativeaot-debug.a, etc. to $(NativeRuntimeOutputRootDir).

I'll look into this next week with @grendello.

</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,43 +14,6 @@ struct DiagnosticSettings {
public bool LogJniGlobalReferences;
private string? GrefPath;

private TextWriter? GrefLrefLog;


public TextWriter? GrefLog {
get {
if (!LogJniGlobalReferences) {
return null;
}
return ((LrefPath != null && LrefPath == GrefPath)
? GrefLrefLog ??= CreateWriter (LrefPath)
: null)
??
((GrefPath != null)
? CreateWriter (GrefPath)
: null)
??
new LogcatTextWriter (AndroidLogLevel.Debug, "NativeAot:GREF");
}
}

public TextWriter? LrefLog {
get {
if (!LogJniLocalReferences) {
return null;
}
return ((LrefPath != null && LrefPath == GrefPath)
? GrefLrefLog ??= CreateWriter (LrefPath)
: null)
??
((LrefPath != null)
? CreateWriter (LrefPath)
: null)
??
new LogcatTextWriter (AndroidLogLevel.Debug, "NativeAot:LREF");
}
}

TextWriter? CreateWriter (string path)
{
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,26 @@ static int JNI_OnLoad (IntPtr vm, IntPtr reserved)
{
try {
AndroidLog.Print (AndroidLogLevel.Info, "JavaInteropRuntime", "JNI_OnLoad()");
LogcatTextWriter.Init ();
return (int) JniVersion.v1_6;
AndroidCryptoNative_InitLibraryOnLoad(vm, reserved);
clr_initialize_on_onload(vm, reserved);
return (int)JniVersion.v1_6;
}
catch (Exception e) {
AndroidLog.Print (AndroidLogLevel.Error, "JavaInteropRuntime", $"JNI_OnLoad() failed: {e}");
return 0;
}
}

[UnmanagedCallersOnly (EntryPoint="JNI_OnUnload")]
[DllImport("*")]
static extern int AndroidCryptoNative_InitLibraryOnLoad (IntPtr vm, IntPtr reserved);

[DllImport("*")]
static extern int clr_initialize_on_onload (IntPtr vm, IntPtr reserved);

[DllImport("*")]
static extern int clr_initialize_on_runtime_init ();

[UnmanagedCallersOnly(EntryPoint = "JNI_OnUnload")]
static void JNI_OnUnload (IntPtr vm, IntPtr reserved)
{
AndroidLog.Print(AndroidLogLevel.Info, "JavaInteropRuntime", "JNI_OnUnload");
Expand All @@ -38,21 +48,20 @@ static void init (IntPtr jnienv, IntPtr klass, IntPtr classLoader)
var settings = new DiagnosticSettings ();
settings.AddDebugDotnetLog ();

var typeManager = new ManagedTypeManager ();
var options = new NativeAotRuntimeOptions {
EnvironmentPointer = jnienv,
ClassLoader = new JniObjectReference (classLoader),
TypeManager = typeManager,
ValueManager = new SimpleValueManager (),
ClassLoader = new JniObjectReference (classLoader, JniObjectReferenceType.Global),
TypeManager = new ManagedTypeManager (),
ValueManager = ManagedValueManager.GetOrCreateInstance (),
UseMarshalMemberBuilder = false,
JniGlobalReferenceLogWriter = settings.GrefLog,
JniLocalReferenceLogWriter = settings.LrefLog,
};
runtime = options.CreateJreVM ();

// Entry point into Mono.Android.dll
JNIEnvInit.InitializeJniRuntime (runtime);

clr_initialize_on_runtime_init ();

transition = new JniTransition (jnienv);

var handler = Java.Lang.Thread.DefaultUncaughtExceptionHandler;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,51 +8,6 @@

namespace Microsoft.Android.Runtime;

internal sealed class LogcatTextWriter : TextWriter {

public static void Init ()
{
// This method is a no-op, but it's necessary to ensure the static
// constructor is executed.
}

static LogcatTextWriter ()
{
Console.SetOut (new LogcatTextWriter (AndroidLogLevel.Info));
Console.SetError (new LogcatTextWriter (AndroidLogLevel.Error));
}

AndroidLogLevel Level;
string Tag;

internal LogcatTextWriter (AndroidLogLevel level, string tag = "NativeAotFromAndroid")
{
Level = level;
Tag = tag;
}

public override Encoding Encoding => Encoding.UTF8;
public override string NewLine => "\n";

public override void WriteLine (string? value)
{
if (value == null) {
AndroidLog.Print (Level, Tag, "");
return;
}
ReadOnlySpan<char> span = value;
while (!span.IsEmpty) {
if (span.IndexOf ('\n') is int n && n < 0) {
break;
}
var line = span.Slice (0, n);
AndroidLog.Print (Level, Tag, line.ToString ());
span = span.Slice (n + 1);
}
AndroidLog.Print (Level, Tag, span.ToString ());
}
}

static class AndroidLog {

[DllImport ("log", EntryPoint = "__android_log_print", CallingConvention = CallingConvention.Cdecl)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,6 @@ class NativeAotRuntimeOptions : JniRuntime.CreationOptions {

public bool IgnoreUnrecognizedOptions {get; set;}

public TextWriter? JniGlobalReferenceLogWriter {get; set;}
public TextWriter? JniLocalReferenceLogWriter {get; set;}

public NativeAotRuntimeOptions ()
{
JniVersion = JniVersion.v1_2;
Expand Down Expand Up @@ -61,8 +58,8 @@ static NativeAotRuntimeOptions CreateJreVM (NativeAotRuntimeOptions builder)
builder.TypeManager ??= new ManagedTypeManager ();
#endif // NET

builder.ValueManager ??= new SimpleValueManager ();
builder.ObjectReferenceManager ??= new ManagedObjectReferenceManager (builder.JniGlobalReferenceLogWriter, builder.JniLocalReferenceLogWriter);
builder.ValueManager ??= ManagedValueManager.GetOrCreateInstance();
builder.ObjectReferenceManager ??= new Android.Runtime.AndroidObjectReferenceManager ();

if (builder.InvocationPointer != IntPtr.Zero || builder.EnvironmentPointer != IntPtr.Zero)
return builder;
Expand Down
2 changes: 1 addition & 1 deletion src/Mono.Android/Android.Runtime/AndroidRuntime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ public AndroidRuntimeOptions (IntPtr jnienv,
}
}

class AndroidObjectReferenceManager : JniRuntime.JniObjectReferenceManager {
public class AndroidObjectReferenceManager : JniRuntime.JniObjectReferenceManager {
public override int GlobalReferenceCount {
get {return RuntimeNativeMethods._monodroid_gref_get ();}
}
Expand Down
4 changes: 4 additions & 0 deletions src/Mono.Android/Android.Runtime/JNIEnvInit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ static Type TypeGetType (string typeName) =>
internal static void InitializeJniRuntime (JniRuntime runtime)
{
androidRuntime = runtime;
gref_gc_threshold = RuntimeNativeMethods._monodroid_max_gref_get ();
if (gref_gc_threshold != int.MaxValue) {
gref_gc_threshold = checked((gref_gc_threshold * 9) / 10);
}
SetSynchronizationContext ();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android.
<AllowPublishAotWithoutRuntimeIdentifier Condition=" '$(AllowPublishAotWithoutRuntimeIdentifier)' == '' ">true</AllowPublishAotWithoutRuntimeIdentifier>
<!-- NativeAOT's targets currently gives an error about cross-compilation -->
<DisableUnsupportedError Condition=" $([MSBuild]::IsOSPlatform('windows')) and '$(DisableUnsupportedError)' == '' ">true</DisableUnsupportedError>
<!-- HACK: make dotnet restore include Microsoft.NETCore.App.Runtime.NativeAOT.linux-bionic-arm64 -->
<!-- HACK: make dotnet restore include Microsoft.NETCore.App.Runtime.NativeAOT.android-arm64 -->
<_IsPublishing Condition=" '$(_IsPublishing)' == '' ">true</_IsPublishing>
</PropertyGroup>

Expand Down Expand Up @@ -55,7 +55,7 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android.
</PropertyGroup>

<Target Name="_AndroidBeforeIlcCompile"
DependsOnTargets="_PrepareLinking"
DependsOnTargets="_PrepareLinking;_ResolveSdks"
BeforeTargets="SetupProperties">
<!-- Example settings from: https://github.com/jonathanpeppers/Android-NativeAOT/blob/ea69d122cdc7de67aa6a5db14b7e560763c63cdd/DotNet/libdotnet.targets -->
<PropertyGroup>
Expand All @@ -68,6 +68,7 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android.
<_NdkSysrootDir>$(_AndroidNdkDirectory)toolchains/llvm/prebuilt/$(_NdkPrebuiltAbi)/sysroot/usr/lib/$(_NdkSysrootAbi)/</_NdkSysrootDir>
<_NdkBinDir>$(_AndroidNdkDirectory)toolchains/llvm/prebuilt/$(_NdkPrebuiltAbi)/bin/</_NdkBinDir>
<CppCompilerAndLinker>clang++</CppCompilerAndLinker>
<CppLinker>clang++</CppLinker>
<ObjCopyName>llvm-objcopy</ObjCopyName>

<!-- Example settings from: https://github.com/xamarin/xamarin-macios/blob/c43d4ea40bc777969e3b158cf46446df292d8449/dotnet/targets/Xamarin.Shared.Sdk.targets#L541-L550 -->
Expand Down Expand Up @@ -110,6 +111,11 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android.
<ResolvedFileToPublish Include="$(_NdkSysrootDir)libc++_shared.so" RuntimeIdentifier="$(RuntimeIdentifier)" />
<!-- Satellite assemblies -->
<IlcSatelliteAssembly Include="$(_OuterIntermediateSatelliteAssembliesWithTargetPath)" />
<!-- Native support libraries -->
<LinkerArg Include="@(RuntimePackAsset->WithMetadataValue('Filename', 'libnet-android-nativeaot-$(Configuration.ToLower())'))" />
<LinkerArg Include="@(RuntimePackAsset->WithMetadataValue('Filename', 'libxa-shared-bits-$(Configuration.ToLower())'))" />
<LinkerArg Include="$(_NdkSysrootDir)libc++_shared.so" />
<DirectPInvoke Include="xa-internal-api" />
</ItemGroup>
</Target>

Expand Down
29 changes: 28 additions & 1 deletion src/native/clr/host/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,17 @@ string(TOLOWER ${CMAKE_BUILD_TYPE} XAMARIN_NET_ANDROID_SUFFIX)
set(XAMARIN_NET_ANDROID_LIB "net-android${CHECKED_BUILD_INFIX}.${XAMARIN_NET_ANDROID_SUFFIX}")
set(XAMARIN_NET_ANDROID_STATIC_LIB "${XAMARIN_NET_ANDROID_LIB}-static")

set(XAMARIN_NET_ANDROID_NATIVEAOT_LIB "net-android-nativeaot${CHECKED_BUILD_INFIX}-${XAMARIN_NET_ANDROID_SUFFIX}")

set(XAMARIN_NET_ANDROID_NATIVEAOT_SOURCES
bridge-processing.cc
gc-bridge.cc
internal-pinvokes.cc
os-bridge.cc
runtime-util.cc
xamarin_getifaddrs.cc
Copy link
Member

@filipnavara filipnavara Aug 12, 2025

Choose a reason for hiding this comment

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

This will produce conflict with #10385. Not a big deal, just wanted to avoid dragging unused code into newly supported runtimes.

)

set(XAMARIN_MONODROID_SOURCES
assembly-store.cc
bridge-processing.cc
Expand Down Expand Up @@ -72,6 +83,16 @@ if(BUILD_STATIC_LIBRARY)
)
endif()

add_library(${XAMARIN_NET_ANDROID_NATIVEAOT_LIB}
STATIC
${XAMARIN_NET_ANDROID_NATIVEAOT_SOURCES}
)
target_compile_definitions(
${XAMARIN_NET_ANDROID_NATIVEAOT_LIB}
PRIVATE
NATIVEAOT
)

macro(lib_target_options TARGET_NAME)
target_compile_definitions(
${TARGET_NAME}
Expand Down Expand Up @@ -156,11 +177,13 @@ macro(lib_target_options TARGET_NAME)
xa::lz4
-landroid
-llog
-lcoreclr
)
endmacro ()

lib_target_options(${XAMARIN_NET_ANDROID_LIB})
target_link_libraries(${XAMARIN_NET_ANDROID_LIB}
-lcoreclr
)
xa_add_compile_definitions(${XAMARIN_NET_ANDROID_LIB})
xa_add_include_directories(${XAMARIN_NET_ANDROID_LIB})

Expand All @@ -175,3 +198,7 @@ if(BUILD_STATIC_LIBRARY)
xa_add_compile_definitions(${XAMARIN_NET_ANDROID_STATIC_LIB})
xa_add_include_directories(${XAMARIN_NET_ANDROID_STATIC_LIB})
endif()

lib_target_options(${XAMARIN_NET_ANDROID_NATIVEAOT_LIB})
xa_add_compile_definitions(${XAMARIN_NET_ANDROID_NATIVEAOT_LIB})
xa_add_include_directories(${XAMARIN_NET_ANDROID_NATIVEAOT_LIB})
27 changes: 23 additions & 4 deletions src/native/clr/host/bridge-processing.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ void BridgeProcessing::initialize_on_runtime_init (JNIEnv *env, jclass runtimeCl
GCUserPeer_class = RuntimeUtil::get_class_from_runtime_field (env, runtimeClass, "mono_android_GCUserPeer", true);
GCUserPeer_ctor = env->GetMethodID (GCUserPeer_class, "<init>", "()V");

GCUserPeerable_class = env->FindClass ("net/dot/jni/GCUserPeerable");
if (GCUserPeerable_class != nullptr) [[likely]] {
GCUserPeerable_class = static_cast<jclass>(OSBridge::lref_to_gref (env, GCUserPeerable_class));
GCUserPeerable_jiAddManagedReference = env->GetMethodID (GCUserPeerable_class, "jiAddManagedReference", "(Ljava/lang/Object;)V");
GCUserPeerable_jiClearManagedReferences = env->GetMethodID (GCUserPeerable_class, "jiClearManagedReferences", "()V");
} else {
log_error (LOG_DEFAULT, "Failed to find net/dot/jni/GCUserPeerable class.");
}

abort_unless (GCUserPeer_class != nullptr && GCUserPeer_ctor != nullptr, "Failed to load mono.android.GCUserPeer!");
}

Expand Down Expand Up @@ -126,10 +135,10 @@ void BridgeProcessing::add_circular_references (const StronglyConnectedComponent

abort_unless (reference_added, [this, &prev, &next] {
jclass prev_java_class = env->GetObjectClass (prev.handle);
const char *prev_class_name = Host::get_java_class_name_for_TypeManager (prev_java_class);
const char *prev_class_name = OSBridge::get_java_class_name_for_TypeManager (prev_java_class);

jclass next_java_class = env->GetObjectClass (next.handle);
const char *next_class_name = Host::get_java_class_name_for_TypeManager (next_java_class);
const char *next_class_name = OSBridge::get_java_class_name_for_TypeManager (next_java_class);

return detail::_format_message (
"Failed to add reference between objects in a strongly connected component: %s -> %s.",
Expand Down Expand Up @@ -157,6 +166,11 @@ bool BridgeProcessing::add_reference (jobject from, jobject to) noexcept
abort_if_invalid_pointer_argument (from, "from");
abort_if_invalid_pointer_argument (to, "to");

if (GCUserPeerable_class != nullptr && env->IsInstanceOf (from, GCUserPeerable_class)) {
env->CallVoidMethod (from, GCUserPeerable_jiAddManagedReference, to);
return true;
}

jclass java_class = env->GetObjectClass (from);
jmethodID add_method_id = env->GetMethodID (java_class, "monodroidAddReference", "(Ljava/lang/Object;)V");

Expand Down Expand Up @@ -197,6 +211,11 @@ void BridgeProcessing::clear_references (jobject handle) noexcept
{
abort_if_invalid_pointer_argument (handle, "handle");

if (GCUserPeerable_class != nullptr && env->IsInstanceOf (handle, GCUserPeerable_class)) {
env->CallVoidMethod (handle, GCUserPeerable_jiClearManagedReferences);
return;
}

jclass java_class = env->GetObjectClass (handle);
jmethodID clear_method_id = env->GetMethodID (java_class, "monodroidClearReferences", "()V");

Expand Down Expand Up @@ -315,7 +334,7 @@ void BridgeProcessing::log_missing_add_references_method ([[maybe_unused]] jclas
return;
}

char *class_name = Host::get_java_class_name_for_TypeManager (java_class);
char *class_name = OSBridge::get_java_class_name_for_TypeManager (java_class);
log_error (LOG_GC, "Missing monodroidAddReferences method for object of class {}", optional_string (class_name));
free (class_name);
#endif
Expand All @@ -331,7 +350,7 @@ void BridgeProcessing::log_missing_clear_references_method ([[maybe_unused]] jcl
return;
}

char *class_name = Host::get_java_class_name_for_TypeManager (java_class);
char *class_name = OSBridge::get_java_class_name_for_TypeManager (java_class);
log_error (LOG_GC, "Missing monodroidClearReferences method for object of class {}", optional_string (class_name));
free (class_name);
#endif
Expand Down
2 changes: 1 addition & 1 deletion src/native/clr/host/gc-bridge.cc
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ void GCBridge::log_handle_context (JNIEnv *env, HandleContext *ctx) noexcept
jobject handle = ctx->control_block->handle;
jclass java_class = env->GetObjectClass (handle);
if (java_class != nullptr) {
char *class_name = Host::get_java_class_name_for_TypeManager (java_class);
char *class_name = OSBridge::get_java_class_name_for_TypeManager (java_class);
log_info (LOG_GC, "gref {:#x} [{}]", reinterpret_cast<intptr_t> (handle), class_name);
free (class_name);
} else {
Expand Down
Loading