[runtime] Set the current directory to the root directory of the app bundle for all platforms in .NET. (#12104)

To have consistent behavior in .NET, set the current directory to the root of
the app bundle for all platforms.

This is a breaking change for legacy Xamarin.Mac, which used to set the
current directory to the Contents/Resources subdirectory, but there's a simple
workaround for customers that depend on the old behavior (change it in Main
themselves), and I believe the consistent experience across platforms warrants
this change.

Note that we already had a breaking change here for macOS/.NET: we were
(unintentionally) setting the current directory to the Contents/MonoBundle
directory, which neither matched mobile platforms, nor the legacy Xamarin.Mac
behavior.

This solves the problem of what to do for Mac Catalyst apps, because there's
no need to choose between the macOS or the mobile behavior, since they're the
same.

This required changing the launch of macOS apps using CoreCLR to pass the full
path to the entry assembly, since the entry assembly isn't in the current
directory anymore.
This commit is contained in:
Rolf Bjarne Kvinge 2021-07-14 17:42:49 +02:00 коммит произвёл GitHub
Родитель a202354c71
Коммит fc8fb4818c
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
7 изменённых файлов: 52 добавлений и 6 удалений

Просмотреть файл

@ -602,18 +602,18 @@ mono_jit_exec (MonoDomain * domain, MonoAssembly * assembly, int argc, const cha
{
unsigned int exitCode = 0;
char *assemblyName = xamarin_bridge_get_assembly_name (assembly->gchandle);
char *assemblyPath = xamarin_bridge_get_assembly_location (assembly->gchandle);
LOG_CORECLR (stderr, "mono_jit_exec (%p, %p, %i, %p) => EXECUTING %s\n", domain, assembly, argc, argv, assemblyName);
LOG_CORECLR (stderr, "mono_jit_exec (%p, %p, %i, %p) => EXECUTING %s\n", domain, assembly, argc, argv, assemblyPath);
for (int i = 0; i < argc; i++) {
LOG_CORECLR (stderr, " Argument #%i: %s\n", i + 1, argv [i]);
}
int rv = coreclr_execute_assembly (coreclr_handle, coreclr_domainId, argc, argv, assemblyName, &exitCode);
int rv = coreclr_execute_assembly (coreclr_handle, coreclr_domainId, argc, argv, assemblyPath, &exitCode);
LOG_CORECLR (stderr, "mono_jit_exec (%p, %p, %i, %p) => EXECUTING %s rv: %i exitCode: %i\n", domain, assembly, argc, argv, assemblyName, rv, exitCode);
LOG_CORECLR (stderr, "mono_jit_exec (%p, %p, %i, %p) => EXECUTING %s rv: %i exitCode: %i\n", domain, assembly, argc, argv, assemblyPath, rv, exitCode);
xamarin_free (assemblyName);
xamarin_free (assemblyPath);
if (rv != 0)
xamarin_assertion_message ("mono_jit_exec failed: %i\n", rv);

Просмотреть файл

@ -383,6 +383,13 @@
OnlyCoreCLR = true,
},
new XDelegate ("char *", "IntPtr", "xamarin_bridge_get_assembly_location",
"GCHandle", "IntPtr", "gchandle"
) {
WrappedManagedFunction = "GetAssemblyLocation",
OnlyCoreCLR = true,
},
new XDelegate ("MonoObject *", "MonoObject *", "xamarin_bridge_create_exception",
"enum XamarinExceptionTypes", "Runtime.ExceptionType", "type",
"const char *", "IntPtr", "arg0"

Просмотреть файл

@ -270,8 +270,11 @@ xamarin_main (int argc, char *argv[], enum XamarinLaunchMode launch_mode)
MonoAssembly *assembly;
GCHandle exception_gchandle = NULL;
const char *c_bundle_path = xamarin_get_bundle_path ();
// For legacy Xamarin.Mac, we used to chdir to $appdir/Contents/Resources (I'm not sure where this comes from, earliest commit I could find was this: https://github.com/xamarin/maccore/commit/20045dd7f85cb038cea673a9281bb6131711069c)
// For mobile platforms, we chdir to $appdir
// In .NET, we always chdir to $appdir, so that we're consistent
const char *c_bundle_path = xamarin_get_app_bundle_path ();
chdir (c_bundle_path);
setenv ("DYLD_BIND_AT_LAUNCH", "1", 1);

Просмотреть файл

@ -1318,6 +1318,23 @@ xamarin_initialize ()
MONO_EXIT_GC_UNSAFE;
}
static char *x_app_bundle_path = NULL;
const char *
xamarin_get_app_bundle_path ()
{
if (x_app_bundle_path != NULL)
return x_app_bundle_path;
NSBundle *main_bundle = [NSBundle mainBundle];
if (main_bundle == NULL)
xamarin_assertion_message ("Could not find the main bundle in the app ([NSBundle mainBundle] returned nil)");
x_app_bundle_path = strdup ([[[main_bundle bundlePath] stringByStandardizingPath] UTF8String]);
return x_app_bundle_path;
}
static char *x_bundle_path = NULL;
const char *
xamarin_get_bundle_path ()

Просмотреть файл

@ -154,9 +154,12 @@ void xamarin_initialize ();
void xamarin_initialize_embedded (); /* Public API, must not change - this is used by the embeddinator */
void xamarin_assertion_message (const char *msg, ...) __attribute__((__noreturn__));
// Gets the bundle path (where the managed executable is). This is *not* the path of the app bundle (.app/.appex).
const char * xamarin_get_bundle_path (); /* Public API */
// Sets the bundle path (where the managed executable is). By default APP/Contents/MonoBundle.
void xamarin_set_bundle_path (const char *path); /* Public API */
// Gets the app bundle path (.app/.appex).
const char * xamarin_get_app_bundle_path ();
MonoObject * xamarin_get_managed_object_for_ptr_fast (id self, GCHandle *exception_gchandle);
void xamarin_check_for_gced_object (MonoObject *obj, SEL sel, id self, MonoMethod *method, GCHandle *exception_gchandle);
unsigned long xamarin_objc_type_size (const char *type);

Просмотреть файл

@ -407,6 +407,12 @@ namespace ObjCRuntime {
return Marshal.StringToHGlobalAuto (Path.GetFileName (asm.Location));
}
static IntPtr GetAssemblyLocation (IntPtr gchandle)
{
var asm = (Assembly) GetGCHandleTarget (gchandle);
return Marshal.StringToHGlobalAuto (asm.Location);
}
static void SetFlagsForNSObject (IntPtr gchandle, byte flags)
{
var obj = (NSObject) GetGCHandleTarget (gchandle);

Просмотреть файл

@ -847,5 +847,15 @@ Additional information:
handles [i].Free ();
}
}
[Test]
public void CurrentDirectory ()
{
#if NET || !MONOMAC
Assert.AreEqual (Environment.CurrentDirectory, NSBundle.MainBundle.BundlePath, "Current directory at launch");
#else
Assert.AreEqual (Environment.CurrentDirectory, NSBundle.MainBundle.ResourcePath, "Current directory at launch");
#endif
}
}
}