From 1855894e1a8f11beb421d07c60f7e177ea5e9014 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Thu, 6 Mar 2025 13:06:26 -0600 Subject: [PATCH] [nativeaot] set `$HOME` and `$TMPDIR` Context: https://github.com/dotnet/runtime/blob/d09a42b9149e14a37c8ef8363cdb7e17bb922c37/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs#L83 `System.IO.Path.GetTempPath()` doesn't work under NativeAOT as seen by the test failure: Mono.Android.NET_Tests, Android.RuntimeTests.InputStreamInvokerTest.InputStreamTest / Release System.IO.DirectoryNotFoundException : IO_PathNotFound_Path, /tmp/ at Interop.ThrowExceptionForIoErrno(Interop.ErrorInfo, String, Boolean) + 0x11 at Interop.CheckIo(Int64, String, Boolean) + 0x5d at System.IO.Path.GetTempFileName() + 0x14d at Android.RuntimeTests.InputStreamInvokerTest.InputStreamTest() + 0x18 at libMono.Android.NET-Tests!+0x1483023 at System.Reflection.DynamicInvokeInfo.Invoke(Object, IntPtr, Object[], BinderBundle, Boolean) + 0xf3 On Unix-like systems, the BCL just reads `$TMPDIR` and falls back to `/tmp/` if it doesn't exist. `$TMPDIR` is *not* set by default in Android applications. To fix this, we can set `$TMPDIR` to `context.getCacheDir()` just as we do in Mono: https://github.com/dotnet/android/blob/04cd6f40989a8fad30d9a9759aec02d540cc9478/src/java-runtime/java/mono/android/MonoPackageManager.java#L53-L54 https://github.com/dotnet/android/blob/04cd6f40989a8fad30d9a9759aec02d540cc9478/src/native/mono/monodroid/monodroid-glue.cc#L1444-L1445 I think we can simply read these values in Java at startup, and call `Os.setenv()` to set them. There does not seem to be a need to pass these paths into managed code. The `Android.RuntimeTests.InputStreamInvokerTest.InputStreamTest()` test now passes with these changes. I fixed `$HOME` as well, as it will be used by other APIs in the BCL. --- .../Resources/NativeAotRuntimeProvider.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/Xamarin.Android.Build.Tasks/Resources/NativeAotRuntimeProvider.java b/src/Xamarin.Android.Build.Tasks/Resources/NativeAotRuntimeProvider.java index f437be4ca28..a3acd8935af 100644 --- a/src/Xamarin.Android.Build.Tasks/Resources/NativeAotRuntimeProvider.java +++ b/src/Xamarin.Android.Build.Tasks/Resources/NativeAotRuntimeProvider.java @@ -1,5 +1,7 @@ package net.dot.jni.nativeaot; +import android.system.ErrnoException; +import android.system.Os; import android.util.Log; import net.dot.android.ApplicationRegistration; @@ -24,6 +26,18 @@ public void attachInfo(android.content.Context context, android.content.pm.Provi if (context instanceof android.app.Application) { ApplicationRegistration.Context = context; } + + // Set environment variables + try { + String filesDir = context.getFilesDir().getAbsolutePath(); + String cacheDir = context.getCacheDir().getAbsolutePath(); + Os.setenv("HOME", filesDir, true); + Os.setenv("TMPDIR", cacheDir, true); + } catch (ErrnoException e) { + Log.e(TAG, "Failed to set environment variables", e); + } + + // Initialize .NET runtime JavaInteropRuntime.init(); // NOTE: only required for custom applications ApplicationRegistration.registerApplications();