688 строки
20 KiB
Objective-C
688 строки
20 KiB
Objective-C
// vim: set filetype=objc :
|
|
|
|
#include <dlfcn.h> // dlsym
|
|
|
|
#import <Cocoa/Cocoa.h>
|
|
|
|
#include "shared.h"
|
|
#include "product.h"
|
|
#include "xamarin/xamarin.h"
|
|
#include "xamarin/launch.h"
|
|
#include "launcher.h"
|
|
#include "runtime-internal.h"
|
|
#include "main-internal.h"
|
|
|
|
#ifdef DYNAMIC_MONO_RUNTIME
|
|
#define DEFAULT_MONO_RUNTIME "/Library/Frameworks/Mono.framework/Versions/Current"
|
|
static const char *mono_runtime_prefix = NULL;
|
|
bool xamarin_enable_debug = 0;
|
|
#endif
|
|
|
|
static char original_working_directory_path [MAXPATHLEN];
|
|
|
|
extern "C" const char * const
|
|
xamarin_get_original_working_directory_path ()
|
|
{
|
|
return original_working_directory_path;
|
|
}
|
|
|
|
static int
|
|
redirect_io (int from_fd, const char *to_path)
|
|
{
|
|
int err;
|
|
int fd;
|
|
|
|
if ((fd = open (to_path, O_CREAT | O_TRUNC | O_WRONLY, 0644)) == -1)
|
|
return -1;
|
|
|
|
if (dup2 (fd, from_fd) == -1) {
|
|
err = errno;
|
|
close (fd);
|
|
errno = err;
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
init_logdir (void)
|
|
{
|
|
const char *env;
|
|
size_t dirlen;
|
|
char *path;
|
|
|
|
if ((env = getenv ("MONOMAC_LOGDIR")) != NULL && *env) {
|
|
// Redirect stdout/err to log files...
|
|
NSError *error = nil;
|
|
if (![[NSFileManager defaultManager]
|
|
createDirectoryAtPath: [NSString stringWithUTF8String: env]
|
|
withIntermediateDirectories: YES
|
|
attributes: @{ NSFilePosixPermissions: [NSNumber numberWithInt: 0755] }
|
|
error: &error]) {
|
|
fprintf (stderr, PRODUCT ": Could not create log directory: %s\n", [[error description] UTF8String]);
|
|
return;
|
|
}
|
|
|
|
dirlen = strlen (env);
|
|
path = (char *) malloc (dirlen + 12);
|
|
strcpy (path, env);
|
|
|
|
if (path[dirlen - 1] != '/')
|
|
path[dirlen++] = '/';
|
|
|
|
strcpy (path + dirlen, "stdout.log");
|
|
if (redirect_io (STDOUT_FILENO, path) == -1)
|
|
fprintf (stderr, PRODUCT ": Could not redirect stdout to `%s': %s\n", path, strerror (errno));
|
|
|
|
strcpy (path + dirlen, "stderr.log");
|
|
if (redirect_io (STDERR_FILENO, path) == -1)
|
|
fprintf (stderr, PRODUCT ": Could not redirect stderr to `%s': %s\n", path, strerror (errno));
|
|
|
|
free (path);
|
|
}
|
|
}
|
|
|
|
static char *
|
|
decode_qstring (unsigned char **in, unsigned char qchar)
|
|
{
|
|
unsigned char *inptr = *in;
|
|
unsigned char *start = *in;
|
|
char *value, *v;
|
|
size_t len = 0;
|
|
|
|
while (*inptr) {
|
|
if (*inptr == qchar)
|
|
break;
|
|
|
|
if (*inptr == '\\') {
|
|
if (inptr[1] == '\0')
|
|
break;
|
|
|
|
inptr++;
|
|
}
|
|
|
|
inptr++;
|
|
len++;
|
|
}
|
|
|
|
v = value = (char *) malloc (len + 1);
|
|
while (start < inptr) {
|
|
if (*start == '\\')
|
|
start++;
|
|
|
|
*v++ = (char) *start++;
|
|
}
|
|
|
|
*v = '\0';
|
|
|
|
if (*inptr)
|
|
inptr++;
|
|
|
|
*in = inptr;
|
|
|
|
return value;
|
|
}
|
|
|
|
typedef struct _ListNode {
|
|
struct _ListNode *next;
|
|
char *value;
|
|
} ListNode;
|
|
|
|
static char **
|
|
get_mono_env_options (int *count)
|
|
{
|
|
const char *env = getenv ("MONO_ENV_OPTIONS");
|
|
ListNode *list = NULL, *node, *tail = NULL;
|
|
unsigned char *start, *inptr;
|
|
char *value, **argv;
|
|
int i, n = 0;
|
|
size_t size;
|
|
|
|
if (env == NULL) {
|
|
*count = 0;
|
|
return NULL;
|
|
}
|
|
|
|
inptr = (unsigned char *) env;
|
|
|
|
while (*inptr) {
|
|
while (isblank ((int) *inptr))
|
|
inptr++;
|
|
|
|
if (*inptr == '\0')
|
|
break;
|
|
|
|
start = inptr++;
|
|
switch (*start) {
|
|
case '\'':
|
|
case '"':
|
|
value = decode_qstring (&inptr, *start);
|
|
break;
|
|
default:
|
|
while (*inptr && !isblank ((int) *inptr))
|
|
inptr++;
|
|
|
|
// Note: Mac OS X <= 10.6.8 do not have strndup()
|
|
//value = strndup ((char *) start, (size_t) (inptr - start));
|
|
size = (size_t) (inptr - start);
|
|
value = (char *) malloc (size + 1);
|
|
memcpy (value, start, size);
|
|
value[size] = '\0';
|
|
break;
|
|
}
|
|
|
|
node = (ListNode *) malloc (sizeof (ListNode));
|
|
node->value = value;
|
|
node->next = NULL;
|
|
n++;
|
|
|
|
if (tail != NULL)
|
|
tail->next = node;
|
|
else
|
|
list = node;
|
|
|
|
tail = node;
|
|
}
|
|
|
|
// Note: we do not want child processes to inherit this environment variable,
|
|
// so now that we've parsed it (and each value is strdup'd), we can safely
|
|
// unset it.
|
|
unsetenv ("MONO_ENV_OPTIONS");
|
|
|
|
*count = n;
|
|
|
|
if (n == 0)
|
|
return NULL;
|
|
|
|
argv = (char **) malloc (sizeof (char *) * (n + 1));
|
|
i = 0;
|
|
|
|
while (list != NULL) {
|
|
node = list->next;
|
|
argv[i++] = list->value;
|
|
free (list);
|
|
list = node;
|
|
}
|
|
|
|
argv[i] = NULL;
|
|
|
|
return argv;
|
|
}
|
|
|
|
static void
|
|
exit_with_message (const char *reason, const char *argv0, bool request_mono)
|
|
{
|
|
NSString *appName = nil;
|
|
NSDictionary *plist = [[NSBundle mainBundle] infoDictionary];
|
|
if (plist) {
|
|
appName = (NSString *) [plist objectForKey:(NSString *)kCFBundleNameKey];
|
|
}
|
|
if (!appName) {
|
|
appName = [[NSString stringWithUTF8String: argv0] lastPathComponent];
|
|
}
|
|
|
|
NSAlert *alert = [[NSAlert alloc] init];
|
|
[alert setMessageText:[NSString stringWithFormat:@"Could not launch %@", appName]];
|
|
NSString *fmt = request_mono ? @"%s\n\nPlease download and install the latest version of Mono." : @"%s\n";
|
|
NSString *msg = [NSString stringWithFormat:fmt, reason];
|
|
[alert setInformativeText:msg];
|
|
NSLog (@"%@", msg);
|
|
|
|
if (request_mono) {
|
|
[alert addButtonWithTitle:@"Download Mono Framework"];
|
|
[alert addButtonWithTitle:@"Cancel"];
|
|
} else {
|
|
[alert addButtonWithTitle:@"OK"];
|
|
}
|
|
NSInteger answer = [alert runModal];
|
|
[alert release];
|
|
|
|
if (request_mono && answer == NSAlertFirstButtonReturn) {
|
|
NSString *mono_download_url = @"http://www.mono-project.com/download/stable/";
|
|
CFURLRef url = CFURLCreateWithString (NULL, (CFStringRef) mono_download_url, NULL);
|
|
LSOpenCFURLRef (url, NULL);
|
|
CFRelease (url);
|
|
}
|
|
exit (1);
|
|
}
|
|
|
|
#ifdef DYNAMIC_MONO_RUNTIME
|
|
static int
|
|
check_mono_version (const char *version, const char *req_version)
|
|
{
|
|
char *req_end, *end;
|
|
long req_val, val;
|
|
|
|
while (*req_version) {
|
|
req_val = strtol (req_version, &req_end, 10);
|
|
if (req_version == req_end || (*req_end && *req_end != '.')) {
|
|
fprintf (stderr, "Bad version requirement string '%s'\n", req_end);
|
|
return FALSE;
|
|
}
|
|
|
|
req_version = req_end;
|
|
if (*req_version)
|
|
req_version++;
|
|
|
|
val = strtol (version, &end, 10);
|
|
if (version == end || val < req_val)
|
|
return FALSE;
|
|
|
|
if (val > req_val)
|
|
return TRUE;
|
|
|
|
if (*req_version == '.' && *end != '.')
|
|
return FALSE;
|
|
|
|
version = end + 1;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static int
|
|
push_env (const char *variable, NSString *str_value)
|
|
{
|
|
const char *value = [str_value UTF8String];
|
|
size_t len = strlen (value);
|
|
const char *current;
|
|
int rv;
|
|
|
|
if ((current = getenv (variable)) && *current) {
|
|
char *buf = (char *) malloc (len + strlen (current) + 2);
|
|
memcpy (buf, value, len);
|
|
buf[len] = ':';
|
|
strcpy (buf + len + 1, current);
|
|
rv = setenv (variable, buf, 1);
|
|
free (buf);
|
|
} else {
|
|
rv = setenv (variable, value, 1);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
update_environment (xamarin_initialize_data *data)
|
|
{
|
|
if (xamarin_get_is_mkbundle ())
|
|
return;
|
|
|
|
// 3) Ensure the following environment variables are set: [...]
|
|
NSString *res_dir;
|
|
NSString *monobundle_dir;
|
|
|
|
if (data->launch_mode == XamarinLaunchModeEmbedded) {
|
|
monobundle_dir = [data->app_dir stringByAppendingPathComponent: @"Versions/Current/MonoBundle"];
|
|
res_dir = [data->app_dir stringByAppendingPathComponent: @"Versions/Current/Resources"];
|
|
} else {
|
|
monobundle_dir = [data->app_dir stringByAppendingPathComponent: @"Contents/MonoBundle"];
|
|
res_dir = [data->app_dir stringByAppendingPathComponent: @"Contents/Resources"];
|
|
}
|
|
|
|
#ifdef DYNAMIC_MONO_RUNTIME
|
|
NSString *bin_dir = [data->app_dir stringByAppendingPathComponent: @"Contents/MacOS"];
|
|
|
|
push_env ("DYLD_FALLBACK_LIBRARY_PATH", [NSString stringWithFormat: @"%s/lib:/usr/local/lib:/lib:/usr/lib", getenv ("HOME")]);
|
|
push_env ("DYLD_FALLBACK_LIBRARY_PATH", [res_dir stringByAppendingPathComponent: @"/lib"]);
|
|
push_env ("DYLD_FALLBACK_LIBRARY_PATH", [[NSString stringWithUTF8String: mono_runtime_prefix] stringByAppendingPathComponent: @"/lib"]);
|
|
|
|
/* Mono "External" directory */
|
|
push_env ("PKG_CONFIG_PATH", @"/Library/Frameworks/Mono.framework/External/pkgconfig");
|
|
/* Enable the use of stuff bundled into the app bundle */
|
|
push_env ("PKG_CONFIG_PATH", [res_dir stringByAppendingPathComponent: @"/lib/pkgconfig"]);
|
|
push_env ("PKG_CONFIG_PATH", [res_dir stringByAppendingPathComponent: @"/share/pkgconfig"]);
|
|
|
|
|
|
push_env ("MONO_GAC_PREFIX", res_dir);
|
|
push_env ("PATH", bin_dir);
|
|
|
|
data->requires_relaunch = true;
|
|
#else
|
|
// disable /dev/shm since Apple refuse applications that uses it in the Mac App Store
|
|
setenv ("MONO_DISABLE_SHARED_AREA", "", 1);
|
|
unsetenv ("MONO_PATH");
|
|
mono_set_assemblies_path (xamarin_get_bundle_path ());
|
|
|
|
// 3b) (If embedding Mono) Change the current directory to $appdir/Contents/Resources
|
|
chdir ([res_dir UTF8String]);
|
|
|
|
// Set up environment variables that need to point to ~/Library/Application Support (registry path)
|
|
NSFileManager *mgr = [NSFileManager defaultManager];
|
|
NSArray *appSupportDirectories = [mgr URLsForDirectory:NSApplicationSupportDirectory inDomains:NSSystemDomainMask];
|
|
|
|
if ([appSupportDirectories count] > 0) {
|
|
NSString *appBundleID = [[NSBundle mainBundle] bundleIdentifier];
|
|
NSURL *appSupport = [appSupportDirectories objectAtIndex: 0];
|
|
if (appSupport != nil && appBundleID != nil) {
|
|
NSURL *appDirectory = [appSupport URLByAppendingPathComponent:appBundleID isDirectory: YES];
|
|
setenv ("MONO_REGISTRY_PATH", [[appDirectory path] UTF8String], 1);
|
|
}
|
|
}
|
|
#endif
|
|
if (xamarin_disable_lldb_attach) {
|
|
// Unfortunately the only place to set debug_options.no_gdb_backtrace is in mini_parse_debug_option
|
|
// So route through MONO_DEBUG
|
|
setenv ("MONO_DEBUG", "no-gdb-backtrace", 0);
|
|
}
|
|
|
|
#ifndef DYNAMIC_MONO_RUNTIME
|
|
setenv ("MONO_CFG_DIR", [monobundle_dir UTF8String], 0);
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
app_initialize (xamarin_initialize_data *data)
|
|
{
|
|
// The launch code here is publicly documented in xamarin/launch.h
|
|
// The numbers in the comments refer to numbers in xamarin/launch.h.
|
|
|
|
xamarin_launch_mode = data->launch_mode;
|
|
|
|
#ifndef SYSTEM_LAUNCHER
|
|
if (xammac_setup ()) {
|
|
data->exit_code = -1;
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
bool mkbundle = xamarin_get_is_mkbundle ();
|
|
|
|
NSBundle *bundle;
|
|
|
|
if (data->launch_mode == XamarinLaunchModeEmbedded) {
|
|
bundle = [NSBundle bundleForClass: [XamarinAssociatedObject class]];
|
|
} else {
|
|
bundle = [NSBundle mainBundle];
|
|
}
|
|
data->app_dir = [bundle bundlePath]; // this is good until the autorelease pool releases the bundlePath string.
|
|
|
|
// 1) If found, call the custom initialization function (xamarin_custom_initialize)
|
|
xamarin_custom_initialize_func init = (xamarin_custom_initialize_func) dlsym (RTLD_MAIN_ONLY, "xamarin_app_initialize");
|
|
if (init != NULL)
|
|
init (data);
|
|
|
|
#ifdef DYNAMIC_MONO_RUNTIME
|
|
// 2) (If not embedding Mono) Search for the system Mono in the following directories:
|
|
if (!(mono_runtime_prefix = getenv ("MONO_RUNTIME")))
|
|
mono_runtime_prefix = DEFAULT_MONO_RUNTIME;
|
|
#endif
|
|
|
|
// 3) Ensure the following environment variables are set: [...]
|
|
if (!data->is_relaunch) {
|
|
update_environment (data);
|
|
|
|
if (data->requires_relaunch)
|
|
return;
|
|
}
|
|
|
|
data->app_dir = NULL; // Make sure nobody ends up using this.
|
|
|
|
// 4) Ensure that the maximum number of open files is least 1024.
|
|
struct rlimit limit;
|
|
if (getrlimit (RLIMIT_NOFILE, &limit) == 0 && limit.rlim_cur < 1024) {
|
|
limit.rlim_cur = MIN (limit.rlim_max, 1024);
|
|
setrlimit (RLIMIT_NOFILE, &limit);
|
|
}
|
|
|
|
NSDictionary *plist = [[NSBundle mainBundle] infoDictionary];
|
|
#ifdef DYNAMIC_MONO_RUNTIME
|
|
// 5) Verify the minimum Mono version. The minimum mono version is specified in: [...]
|
|
NSString *minVersion = NULL;
|
|
if (plist != NULL) {
|
|
minVersion = (NSString *) [plist objectForKey:@"MonoMinimumVersion"];
|
|
if (minVersion == NULL)
|
|
minVersion = (NSString *) [plist objectForKey:@"MonoMinVersion"];
|
|
} else {
|
|
fprintf (stderr, PRODUCT ": Could not load Info.plist from the bundle.");
|
|
}
|
|
|
|
if (!minVersion) {
|
|
// This must be kept in sync with mmp's minimum mono version (in driver.cs)
|
|
minVersion = @MIN_XM_MONO_VERSION;
|
|
}
|
|
|
|
char *mono_version;
|
|
const char *err = xamarin_initialize_dynamic_runtime (mono_runtime_prefix);
|
|
if (err) {
|
|
mono_version = xamarin_get_mono_runtime_build_info ();
|
|
if (mono_version && !check_mono_version (mono_version, [minVersion UTF8String])) {
|
|
exit_with_message ([[NSString stringWithFormat:@"This application requires the Mono framework version %@ or newer.", minVersion] UTF8String], data->basename, true);
|
|
} else {
|
|
exit_with_message (err, data->basename, true);
|
|
}
|
|
}
|
|
|
|
mono_version = mono_get_runtime_build_info ();
|
|
if (!check_mono_version (mono_version, [minVersion UTF8String]))
|
|
exit_with_message ([[NSString stringWithFormat:@"This application requires the Mono framework version %@ or newer.", minVersion] UTF8String], data->basename, true);
|
|
#endif
|
|
|
|
// 6) Find the executable. The name is: [...]
|
|
if (data->launch_mode == XamarinLaunchModeApp) {
|
|
NSString *exeName = NULL;
|
|
NSString *exePath;
|
|
if (plist != NULL)
|
|
exeName = (NSString *) [plist objectForKey:@"MonoBundleExecutable"];
|
|
else
|
|
fprintf (stderr, PRODUCT ": Could not find Info.plist in the bundle.\n");
|
|
|
|
if (exeName == NULL)
|
|
exeName = [[NSString stringWithUTF8String: data->basename] stringByAppendingString: @".exe"];
|
|
|
|
if (mkbundle) {
|
|
exePath = exeName;
|
|
} else {
|
|
exePath = [[[NSString stringWithUTF8String: xamarin_get_bundle_path ()] stringByAppendingString: @"/"] stringByAppendingString: exeName];
|
|
|
|
if (!xamarin_file_exists ([exePath UTF8String]))
|
|
exit_with_message ([[NSString stringWithFormat:@"Could not find the executable '%@'\n\nFull path: %@", exeName, exePath] UTF8String], data->basename, false);
|
|
}
|
|
xamarin_entry_assembly_path = strdup ([exePath UTF8String]);
|
|
} else {
|
|
NSString *dllName = [[NSString stringWithUTF8String: data->basename] stringByAppendingString: @".dll"];
|
|
NSString *dllPath = [[[NSString stringWithUTF8String: xamarin_get_bundle_path ()] stringByAppendingString: @"/"] stringByAppendingString: dllName];
|
|
if (!xamarin_file_exists ([dllPath UTF8String]))
|
|
exit_with_message ([[NSString stringWithFormat:@"Could not find the extension library '%@'\n\nFull path: %@", dllName, dllPath] UTF8String], data->basename, false);
|
|
|
|
xamarin_entry_assembly_path = strdup ([dllPath UTF8String]);
|
|
}
|
|
|
|
// 7a) [If not embedding] Don't parse any config files, leave it to the Mono runtime to load the defaults.
|
|
// 7b) [If embedding] Parse $appdir/Contents/MonoBundle/machine.config and $appdir/Contents/MonoBundle/config
|
|
if (!mkbundle) {
|
|
NSString *config_path = nil;
|
|
NSString *machine_config_path = nil;
|
|
NSError *error = nil;
|
|
|
|
#ifndef DYNAMIC_MONO_RUNTIME
|
|
config_path = [[NSString stringWithUTF8String: xamarin_get_bundle_path ()] stringByAppendingPathComponent: @"config"];
|
|
machine_config_path = [[NSString stringWithUTF8String: xamarin_get_bundle_path ()] stringByAppendingPathComponent: @"machine.config"];
|
|
mono_set_dirs (xamarin_get_bundle_path (), xamarin_get_bundle_path ());
|
|
#else
|
|
mono_set_dirs (NULL, NULL);
|
|
#endif
|
|
|
|
if (machine_config_path != nil) {
|
|
NSString *config = [NSString stringWithContentsOfFile: machine_config_path encoding: NSUTF8StringEncoding error: &error];
|
|
if (config != nil) {
|
|
mono_register_machine_config (strdup ([config UTF8String]));
|
|
} else {
|
|
// fprintf (stderr, PRODUCT ": Could not load machine.config: %s\n", [machine_config_path UTF8String]);
|
|
}
|
|
}
|
|
|
|
if (config_path != nil) {
|
|
NSString *config = [NSString stringWithContentsOfFile: config_path encoding: NSUTF8StringEncoding error: &error];
|
|
if (config != nil) {
|
|
mono_config_parse_memory ((char *) [config UTF8String]);
|
|
} else {
|
|
// fprintf (stderr, PRODUCT ": Could not load config: %s\n", [config_path UTF8String]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* other non-documented stuff... */
|
|
|
|
xamarin_initialize_cocoa_threads (NULL);
|
|
init_logdir ();
|
|
mono_set_signal_chaining (TRUE);
|
|
mono_set_crash_chaining (TRUE);
|
|
}
|
|
|
|
#define __XAMARIN_MAC_RELAUNCH_APP__ "__XAMARIN_MAC_RELAUNCH_APP__"
|
|
|
|
static void
|
|
run_application_init (xamarin_initialize_data *data)
|
|
{
|
|
if (!xamarin_file_exists (xamarin_entry_assembly_path))
|
|
exit_with_message ([[NSString stringWithFormat:@"Could not find the assembly '%s'", xamarin_entry_assembly_path] UTF8String], data->basename, false);
|
|
|
|
// Make sure any output from mono isn't lost when launching extensions,
|
|
// etc, by installing the log callbacks early (xamarin_initialize will
|
|
// also do this, but if something goes wrong before we reach
|
|
// xamarin_initialize when running as an extension, the output will be
|
|
// lost).
|
|
xamarin_install_log_callbacks ();
|
|
|
|
mono_jit_init (xamarin_entry_assembly_path);
|
|
|
|
MonoAssembly *assembly = xamarin_open_assembly ("Xamarin.Mac.dll");
|
|
if (!assembly)
|
|
xamarin_assertion_message ("Failed to load %s.", "Xamarin.Mac.dll");
|
|
|
|
MonoImage *image = mono_assembly_get_image (assembly);
|
|
|
|
MonoClass *app_class = mono_class_from_name (image, "AppKit", "NSApplication");
|
|
if (!app_class)
|
|
xamarin_assertion_message ("Fatal error: failed to load the NSApplication class");
|
|
|
|
MonoMethod *initialize = mono_class_get_method_from_name (app_class, "Init", 0);
|
|
if (!initialize)
|
|
xamarin_assertion_message ("Fatal error: failed to load the NSApplication.Init method");
|
|
|
|
mono_runtime_invoke (initialize, NULL, NULL, NULL);
|
|
}
|
|
|
|
int xamarin_main (int argc, char **argv, enum XamarinLaunchMode launch_mode)
|
|
{
|
|
xamarin_initialize_data data = { 0 };
|
|
|
|
if (getcwd (original_working_directory_path, sizeof (original_working_directory_path)) == NULL)
|
|
original_working_directory_path [0] = '\0';
|
|
|
|
@autoreleasepool {
|
|
data.size = sizeof (data);
|
|
data.argc = argc;
|
|
data.argv = argv;
|
|
data.launch_mode = launch_mode;
|
|
// basename = Path.GetFileName (argv [0])
|
|
if (!(data.basename = strrchr (argv [0], '/'))) {
|
|
data.basename = argv [0];
|
|
} else {
|
|
data.basename++;
|
|
}
|
|
|
|
data.is_relaunch = getenv (__XAMARIN_MAC_RELAUNCH_APP__) != NULL;
|
|
if (data.is_relaunch)
|
|
unsetenv (__XAMARIN_MAC_RELAUNCH_APP__);
|
|
|
|
app_initialize (&data);
|
|
|
|
if (data.exit)
|
|
return data.exit_code;
|
|
|
|
if (data.requires_relaunch) {
|
|
setenv (__XAMARIN_MAC_RELAUNCH_APP__, "1", 1);
|
|
return execv (argv [0], argv);
|
|
}
|
|
}
|
|
|
|
int rv = 0;
|
|
|
|
@autoreleasepool {
|
|
int env_argc = 0;
|
|
char **env_argv = get_mono_env_options (&env_argc);
|
|
int new_argc = env_argc + 3 /* --use-map-jit --debug executable */ + argc ;
|
|
if (xamarin_mac_hybrid_aot)
|
|
new_argc += 1;
|
|
if (xamarin_mac_modern)
|
|
new_argc += 1;
|
|
|
|
char **new_argv = (char **) malloc (sizeof (char *) * (new_argc + 1 /* null terminated */));
|
|
const char **ptr = (const char **) new_argv;
|
|
// binary
|
|
*ptr++ = argv [0];
|
|
*ptr++ = "--use-map-jit";
|
|
|
|
// inject MONO_ENV_OPTIONS
|
|
for (int i = 0; i < env_argc; i++)
|
|
*ptr++ = env_argv [i];
|
|
|
|
if (xamarin_debug_mode) {
|
|
*ptr++ = "--debug";
|
|
} else {
|
|
new_argc--;
|
|
}
|
|
|
|
if (xamarin_mac_hybrid_aot)
|
|
*ptr++ = "--hybrid-aot";
|
|
if (xamarin_mac_modern)
|
|
*ptr++ = "--runtime=mobile";
|
|
|
|
// executable assembly
|
|
*ptr++ = xamarin_entry_assembly_path;
|
|
|
|
// the rest
|
|
for (int i = 1; i < argc; i++)
|
|
*ptr++ = argv [i];
|
|
*ptr = NULL;
|
|
|
|
switch (launch_mode) {
|
|
case XamarinLaunchModeExtension: {
|
|
run_application_init (&data);
|
|
|
|
void * libExtensionHandle = dlopen ("/usr/lib/libextension.dylib", RTLD_LAZY);
|
|
if (libExtensionHandle == nil)
|
|
exit_with_message ("Unable to load libextension.dylib", data.basename, false);
|
|
|
|
typedef int (*extension_main)(int argc, char * argv[]);
|
|
|
|
extension_main extensionMain = (extension_main) dlsym (libExtensionHandle, "NSExtensionMain");
|
|
|
|
if (extensionMain == nil)
|
|
exit_with_message ("Unable to load NSExtensionMain", data.basename, false);
|
|
|
|
rv = (*extensionMain) (new_argc, new_argv);
|
|
dlclose (libExtensionHandle);
|
|
break;
|
|
}
|
|
case XamarinLaunchModeApp:
|
|
rv = mono_main (new_argc, new_argv);
|
|
break;
|
|
case XamarinLaunchModeEmbedded:
|
|
run_application_init (&data);
|
|
break;
|
|
default:
|
|
xamarin_assertion_message ("Invalid launch mode: %i.", launch_mode);
|
|
break;
|
|
}
|
|
|
|
free (new_argv);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
int main (int argc, char **argv)
|
|
{
|
|
return xamarin_main (argc, argv, XamarinLaunchModeApp);
|
|
}
|
|
|
|
int xamarin_mac_extension_main (int argc, char **argv)
|
|
{
|
|
return xamarin_main (argc, argv, XamarinLaunchModeExtension);
|
|
}
|