diff --git a/packaging/macos/buildpackage.sh b/packaging/macos/buildpackage.sh index b9d7ffe506..c25657df1f 100755 --- a/packaging/macos/buildpackage.sh +++ b/packaging/macos/buildpackage.sh @@ -124,16 +124,24 @@ modify_plist "{DEV_VERSION}" "${TAG}" "${TEMPLATE_DIR}/Contents/Info.plist" modify_plist "{FAQ_URL}" "http://wiki.openra.net/FAQ" "${TEMPLATE_DIR}/Contents/Info.plist" modify_plist "{MINIMUM_SYSTEM_VERSION}" "10.11" "${TEMPLATE_DIR}/Contents/Info.plist" -# Compile universal (x86_64 + arm64) Launcher and arch-specific apphosts +# Compile universal (x86_64 + arm64) arch-specific apphosts clang apphost.c -o "${TEMPLATE_DIR}/Contents/MacOS/apphost-x86_64" -framework AppKit -target x86_64-apple-macos10.15 clang apphost.c -o "${TEMPLATE_DIR}/Contents/MacOS/apphost-arm64" -framework AppKit -target arm64-apple-macos10.15 clang apphost-mono.c -o "${TEMPLATE_DIR}/Contents/MacOS/apphost-mono" -framework AppKit -target x86_64-apple-macos10.11 clang checkmono.c -o "${TEMPLATE_DIR}/Contents/MacOS/checkmono" -framework AppKit -target x86_64-apple-macos10.11 + +# Compile universal (x86_64 + arm64) Launcher clang launcher.m -o "${TEMPLATE_DIR}/Contents/MacOS/Launcher-x86_64" -framework AppKit -target x86_64-apple-macos10.11 clang launcher.m -o "${TEMPLATE_DIR}/Contents/MacOS/Launcher-arm64" -framework AppKit -target arm64-apple-macos10.15 lipo -create -output "${TEMPLATE_DIR}/Contents/MacOS/Launcher" "${TEMPLATE_DIR}/Contents/MacOS/Launcher-x86_64" "${TEMPLATE_DIR}/Contents/MacOS/Launcher-arm64" rm "${TEMPLATE_DIR}/Contents/MacOS/Launcher-x86_64" "${TEMPLATE_DIR}/Contents/MacOS/Launcher-arm64" +# Compile universal (x86_64 + arm64) Utility +clang utility.m -o "${TEMPLATE_DIR}/Contents/MacOS/Utility-x86_64" -framework AppKit -target x86_64-apple-macos10.11 +clang utility.m -o "${TEMPLATE_DIR}/Contents/MacOS/Utility-arm64" -framework AppKit -target arm64-apple-macos10.15 +lipo -create -output "${TEMPLATE_DIR}/Contents/MacOS/Utility" "${TEMPLATE_DIR}/Contents/MacOS/Utility-x86_64" "${TEMPLATE_DIR}/Contents/MacOS/Utility-arm64" +rm "${TEMPLATE_DIR}/Contents/MacOS/Utility-x86_64" "${TEMPLATE_DIR}/Contents/MacOS/Utility-arm64" + build_app "${TEMPLATE_DIR}" "${BUILTDIR}/OpenRA - Red Alert.app" "ra" "Red Alert" "699222659766026240" build_app "${TEMPLATE_DIR}" "${BUILTDIR}/OpenRA - Tiberian Dawn.app" "cnc" "Tiberian Dawn" "699223250181292033" build_app "${TEMPLATE_DIR}" "${BUILTDIR}/OpenRA - Dune 2000.app" "d2k" "Dune 2000" "712711732770111550" diff --git a/packaging/macos/utility.m b/packaging/macos/utility.m new file mode 100644 index 0000000000..35636ba6a4 --- /dev/null +++ b/packaging/macos/utility.m @@ -0,0 +1,227 @@ +/* + * Copyright (c) The OpenRA Developers and Contributors + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation. For more information, + * see COPYING. + */ + +#import +#include +#include +#include +#include +#include +#include +#include + +#define SYSTEM_MONO_PATH @"/Library/Frameworks/Mono.framework/Versions/Current/" +#define SYSTEM_MONO_MIN_VERSION @"6.4" +#define DOTNET_MIN_MACOS_VERSION 10.15 + +typedef void* hostfxr_handle; +struct hostfxr_initialize_parameters +{ + size_t size; + char *host_path; + char *dotnet_root; +}; + +typedef int32_t(*hostfxr_initialize_for_dotnet_command_line_fn)( + int argc, + char **argv, + struct hostfxr_initialize_parameters *parameters, + hostfxr_handle *host_context_handle); + +typedef int32_t(*hostfxr_run_app_fn)(const hostfxr_handle host_context_handle); +typedef int32_t(*hostfxr_close_fn)(const hostfxr_handle host_context_handle); +typedef int (*mono_main)(int argc, char **argv); + +int launch_mono(int argc, char **argv, char *modId) +{ + NSString *exePath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent: @"Contents/MacOS/"]; + NSString *hostPath = [SYSTEM_MONO_PATH stringByAppendingPathComponent: @"lib/libmonosgen-2.0.dylib"]; + NSString *dllPath = [exePath stringByAppendingPathComponent: @"mono/OpenRA.Utility.dll"]; + + // TODO: This snippet increasing the open file limit was copied from + // the monodevelop launcher stub. It may not be needed for OpenRA. + struct rlimit limit; + if (getrlimit(RLIMIT_NOFILE, &limit) == 0 && limit.rlim_cur < 1024) + { + limit.rlim_cur = limit.rlim_max < 1024 ? limit.rlim_max : 1024; + setrlimit(RLIMIT_NOFILE, &limit); + } + + void *libmono = dlopen([hostPath UTF8String], RTLD_LAZY); + if (!libmono) + { + NSLog(@"Failed to load libmonosgen-2.0.dylib: %s\n", dlerror()); + return 1; + } + + mono_main _mono_main = (mono_main)dlsym(libmono, "mono_main"); + if (!_mono_main) + { + NSLog(@"Could not load mono_main(): %s\n", dlerror()); + return 1; + } + + // Insert hostpath, dll and modId as arguments. Overwrite the first argument which was used to launch this application. + char **newv = malloc((argc + 2) * sizeof(char*)); + if (!newv) + { + NSLog(@"Failed to allocate memory for args array.\n"); + return 1; + } + + newv[0] = (char*)[hostPath UTF8String]; + newv[1] = (char*)[dllPath UTF8String]; + newv[2] = modId; + for (int i = 1; i < argc; i++) + newv[i + 2] = argv[i]; + + int ret = _mono_main(argc + 2, newv); + free(newv); + + return ret; +} + +int launch_dotnet(int argc, char **argv, char *modId, bool isArmArchitecture) +{ + NSString *exePath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent: @"Contents/MacOS/"]; + NSString *dllPath; + NSString *hostPath; + + if (isArmArchitecture) + { + hostPath = [exePath stringByAppendingPathComponent: @"arm64/libhostfxr.dylib"];; + dllPath = [exePath stringByAppendingPathComponent: @"arm64/OpenRA.Utility.dll"]; + } + else + { + hostPath = [exePath stringByAppendingPathComponent: @"x86_64/libhostfxr.dylib"];; + dllPath = [exePath stringByAppendingPathComponent: @"x86_64/OpenRA.Utility.dll"]; + } + + void *lib = dlopen([hostPath UTF8String], RTLD_LAZY); + if (!lib) + { + NSLog(@"Failed to load %@: %s\n", hostPath, dlerror()); + return 1; + } + + hostfxr_initialize_for_dotnet_command_line_fn hostfxr_initialize_for_dotnet_command_line = (hostfxr_initialize_for_dotnet_command_line_fn)dlsym(lib, "hostfxr_initialize_for_dotnet_command_line"); + if (!hostfxr_initialize_for_dotnet_command_line) + { + NSLog(@"Could not load hostfxr_initialize_for_dotnet_command_line(): %s\n", dlerror()); + return 1; + } + + hostfxr_run_app_fn hostfxr_run_app = (hostfxr_run_app_fn)dlsym(lib, "hostfxr_run_app"); + if (!hostfxr_run_app) + { + NSLog(@"Could not load hostfxr_run_app(): %s\n", dlerror()); + return 1; + } + + hostfxr_close_fn hostfxr_close = (hostfxr_close_fn)dlsym(lib, "hostfxr_close"); + if (!hostfxr_close) + { + NSLog(@"Could not load hostfxr_close(): %s\n", dlerror()); + return 1; + } + + struct hostfxr_initialize_parameters params; + params.size = sizeof(params); + params.host_path = (char*)[[exePath stringByAppendingPathComponent: @"Utility"] UTF8String]; + params.dotnet_root = dirname((char*)[hostPath UTF8String]); + + // Insert dll and modId as arguments. Overwrite the first argument which was used to launch this application. + char **newv = malloc((argc + 1) * sizeof(char*)); + if (!newv) + { + NSLog(@"Failed to allocate memory for args array.\n"); + return 1; + } + + newv[0] = (char*)[dllPath UTF8String]; + newv[1] = modId; + for (int i = 1; i < argc; i++) + newv[i + 1] = argv[i]; + + hostfxr_handle host_context_handle; + hostfxr_initialize_for_dotnet_command_line( + argc + 1, + newv, + ¶ms, + &host_context_handle); + + hostfxr_run_app(host_context_handle); + + int ret = hostfxr_close(host_context_handle); + free(newv); + + return ret; +} + +int main(int argc, char **argv) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + size_t size; + cpu_type_t type; + size = sizeof(type); + bool isArmArchitecture = sysctlbyname("hw.cputype", &type, &size, NULL, 0) == 0 && (type & 0xFF) == CPU_TYPE_ARM; + + BOOL useMono = NO; + + // Before 10.15 macOS didn't support arm. + // Mono is compiled for intel only. + if (!isArmArchitecture) + { + if (@available(macOS 10.15, *)) + useMono = [[[NSProcessInfo processInfo] environment]objectForKey:@"OPENRA_PREFER_MONO"] != nil; + else + useMono = YES; + } + + if (useMono) + { + NSTask *task = [[NSTask alloc] init]; + [task setLaunchPath: [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent: @"Contents/MacOS/checkmono"]]; + [task launch]; + [task waitUntilExit]; + + if ([task terminationStatus] != 0) + { + NSLog(@"Utility requires Mono %@ or later. Please install Mono and try again.\n", SYSTEM_MONO_MIN_VERSION); + return 1; + } + } + + NSDictionary *plist = [[NSBundle mainBundle] infoDictionary]; + char *modId; + if (plist) + { + NSString *modIdValue = [plist objectForKey:@"ModId"]; + if (modIdValue && [modIdValue length] > 0) + modId = (char*)[modIdValue UTF8String]; + } + + if (!modId) + { + NSLog(@"Could not detect ModId\n"); + return 1; + } + + setenv("ENGINE_DIR", (char*)[[[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent: @"Contents/Resources/"] UTF8String], 1); + + int ret; + if (useMono) + ret = launch_mono(argc, argv, modId); + else + ret = launch_dotnet(argc, argv, modId, isArmArchitecture); + + [pool release]; + return ret; +}