From 26c6036893ba4620ef0bf7d52e57b4045fa856b4 Mon Sep 17 00:00:00 2001 From: "Vladimir.Orlov" Date: Fri, 22 Mar 2019 17:59:29 +0300 Subject: [PATCH] mac launcher: search bundled jvm in jdk and jbr folders. --- native/MacLauncher/Launcher.m | 1190 +++++++++-------- .../resources/mac/Contents/MacOS/executable | Bin 83316 -> 83316 bytes 2 files changed, 596 insertions(+), 594 deletions(-) diff --git a/native/MacLauncher/Launcher.m b/native/MacLauncher/Launcher.m index 9a4e8c800ae1..4eaebf1eb660 100644 --- a/native/MacLauncher/Launcher.m +++ b/native/MacLauncher/Launcher.m @@ -1,595 +1,597 @@ -// -// Created by max on 5/4/12. -// -// To change the template use AppCode | Preferences | File Templates. -// - - -#import "Launcher.h" -#import "VMOptionsReader.h" -#import "PropertyFileReader.h" -#import "utils.h" -#import -@class NSAlert; - -typedef jint (JNICALL *fun_ptr_t_CreateJavaVM)(JavaVM **pvm, void **env, void *args); -NSBundle *vm; -NSString *const JVMOptions = @"JVMOptions"; -NSString *JVMVersion = NULL; -NSString* minRequiredJavaVersion = @"1.8"; -NSString* osxVersion = @"10.10"; -BOOL javaUpdateRequired = false; - - -@interface NSString (CustomReplacements) -- (NSString *)replaceAll:(NSString *)pattern to:(NSString *)replacement; - -@end - -@implementation NSString (CustomReplacements) -- (NSString *)replaceAll:(NSString *)pattern to:(NSString *)replacement { - if ([self rangeOfString:pattern].length == 0) return self; - - NSMutableString *answer = [[self mutableCopy] autorelease]; - [answer replaceOccurrencesOfString:pattern withString:replacement options:0 range:NSMakeRange(0, [self length])]; - return answer; -} -@end - -@interface NSDictionary (TypedGetters) -- (NSDictionary *)dictionaryForKey:(id)key; -- (id)valueForKey:(NSString *)key inDictionary:(NSString *)dictKey defaultObject:(NSString *)defaultValue; -@end - -@implementation NSDictionary (TypedGetters) -- (NSDictionary *)dictionaryForKey:(id)key { - id answer = [self objectForKey:key]; - if ([answer isKindOfClass:[NSDictionary class]]) { - return answer; - } - return nil; -} - -- (id)valueForKey:(NSString *)key inDictionary:(NSString *)dictKey defaultObject: (NSString*) defaultValue { - NSDictionary *dict = [self dictionaryForKey:dictKey]; - if (dict == nil) return nil; - id answer = [dict valueForKey:key]; - return answer != nil ? answer : defaultValue; -} -@end - -@implementation Launcher - -- (id)initWithArgc:(int)anArgc argv:(char **)anArgv { - self = [super init]; - if (self) { - argc = anArgc; - argv = anArgv; - } - - return self; -} - -NSString* getOSXVersion(){ - NSString *versionString; - NSDictionary * sv = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]; - versionString = [sv objectForKey:@"ProductVersion"]; - //NSLog(@"OS X: %@", versionString); - return versionString; -} - -void showWarning(NSString* messageText){ - NSAlert* alert = [[NSAlert alloc] init]; - [alert addButtonWithTitle:@"OK"]; - NSString* message_description = [NSString stringWithFormat:@"Java 1.8 or later is required."]; - NSString* informativeText =[NSString stringWithFormat:@"%@",message_description]; - [alert setMessageText:messageText]; - [alert setInformativeText:informativeText ]; - [alert setAlertStyle:NSWarningAlertStyle]; - [alert runModal]; - [alert release]; -} - - -BOOL appendBundle(NSString *path, NSMutableArray *sink) { - if ([path hasSuffix:@"jdk"] || [path hasSuffix:@".jre"]) { - NSBundle *bundle = [NSBundle bundleWithPath:path]; - if (bundle != nil) { - [sink addObject:bundle]; - return true; - } - } - return false; -} - -BOOL appendJvmBundlesAt(NSString *path, NSMutableArray *sink) { - NSError *error = nil; - NSArray *names = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:&error]; - - BOOL res = false; - if (names != nil) { - for (NSString *name in names) { - res |= appendBundle([path stringByAppendingPathComponent:name], sink); - } - } - return res; -} - -NSArray *allVms() { - NSMutableArray *jvmBundlePaths = [NSMutableArray array]; - - // search java info in user's idea.properties - NSString* ideaProperty = getPropertiesFilePath(); - if ([[NSFileManager defaultManager] fileExistsAtPath:ideaProperty]) { - NSDictionary *inConfig =[PropertyFileReader readFile:ideaProperty]; - NSString* userJavaVersion =[inConfig objectForKey:@"JVMVersion"]; - if (userJavaVersion != nil && meetMinRequirements(userJavaVersion)) { - JVMVersion = userJavaVersion; - } - } - NSString *required = requiredJvmVersions(); - NSLog(@"allVms required %@", required); - - if (! jvmBundlePaths.count > 0 ) { - NSBundle *bundle = [NSBundle mainBundle]; - NSString *appDir = [bundle.bundlePath stringByAppendingPathComponent:@"Contents"]; - - if (!appendJvmBundlesAt([appDir stringByAppendingPathComponent:@"/jre"], jvmBundlePaths)) { - appendBundle([appDir stringByAppendingPathComponent:@"/jdk"], jvmBundlePaths); - } - if ((jvmBundlePaths.count > 0) && (satisfies(jvmVersion(jvmBundlePaths[jvmBundlePaths.count-1]), required))) return jvmBundlePaths; - - appendJvmBundlesAt([NSHomeDirectory() stringByAppendingPathComponent:@"Library/Java/JavaVirtualMachines"], jvmBundlePaths); - appendJvmBundlesAt(@"/Library/Java/JavaVirtualMachines", jvmBundlePaths); - appendJvmBundlesAt(@"/System/Library/Java/JavaVirtualMachines", jvmBundlePaths); - } - - return jvmBundlePaths; -} - -NSString *jvmVersion(NSBundle *bundle) { - NSString* javaVersion = [bundle.infoDictionary valueForKey:@"JVMVersion" inDictionary:@"JavaVM" defaultObject:@"0"]; - //NSLog(@"jvmVersion: %@", javaVersion); - return javaVersion; -} - -NSString *requiredJvmVersions() { - return (JVMVersion != NULL) ? JVMVersion : [[NSBundle mainBundle].infoDictionary valueForKey:@"JVMVersion" inDictionary: JVMOptions defaultObject:@"1.8*"]; -} - -BOOL meetMinRequirements (NSString *vmVersion) { - return [minRequiredJavaVersion compare:vmVersion options:NSNumericSearch] <= 0; -} - -BOOL satisfies(NSString *vmVersion, NSString *requiredVersion) { - BOOL meetRequirement = meetMinRequirements(vmVersion); - if (! meetRequirement) { - return meetRequirement; - } - - if ([requiredVersion hasSuffix:@"+"]) { - requiredVersion = [requiredVersion substringToIndex:[requiredVersion length] - 1]; - return [requiredVersion compare:vmVersion options:NSNumericSearch] <= 0; - } - if ([requiredVersion hasSuffix:@"*"]) { - requiredVersion = [requiredVersion substringToIndex:[requiredVersion length] - 1]; - } - return [vmVersion hasPrefix:requiredVersion]; -} - -NSComparisonResult compareVMVersions(id vm1, id vm2, void *context) { - return [jvmVersion(vm2) compare:jvmVersion(vm1) options:NSNumericSearch]; -} - -NSBundle *getJDKBundle(NSString* jdkVersion, NSString* source) { - if (jdkVersion != nil) { - NSBundle *jdkBundle = [NSBundle bundleWithPath : jdkVersion]; - if (jdkBundle != nil && ![jvmVersion(jdkBundle) isEqualToString :@"0"]) { - NSString *javaVersion = jvmVersion(jdkBundle); - if (javaVersion != nil && meetMinRequirements(javaVersion)) { - debugLog(@"VM from:"); - debugLog(source); - debugLog([jdkBundle bundlePath]); - return jdkBundle; - } - } - } - return nil; -} - -NSBundle *findMatchingVm() { - // boot jdk action - NSFileManager *fileManager = [NSFileManager defaultManager]; - - NSString *pathForFile = [NSString stringWithFormat:@"%@/%@.jdk", getPreferencesFolderPath(), getExecutable()]; - - if (!pathForFile.isAbsolutePath) { - // Handle relative paths - pathForFile = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:pathForFile]; - } - - if ([fileManager fileExistsAtPath:pathForFile]) { - NSString* fileContents = [NSString stringWithContentsOfFile:pathForFile encoding:NSUTF8StringEncoding error:nil]; - NSArray* allLinedStrings = [fileContents componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]]; - if (allLinedStrings.count > 0) { - NSString* jdkFromProfile = allLinedStrings[0]; - NSBundle *jdkBundle = getJDKBundle(jdkFromProfile, @"IDE profile"); - if (jdkBundle != nil) { - return jdkBundle; - } - } - } - - //the environment variable. - NSString *variable = [[getExecutable() uppercaseString] stringByAppendingString:@"_JDK"]; - // The explicitly set JDK to use. - NSString *explicit = [[[NSProcessInfo processInfo] environment] objectForKey:variable]; - if (explicit != nil) { - NSLog(@"Value of %@: %@", variable, explicit); - NSBundle *jdkBundle = getJDKBundle(explicit, @"environment variable"); - if (jdkBundle != nil) { - return jdkBundle; - } - } - - NSArray *vmBundles = [allVms() sortedArrayUsingFunction:compareVMVersions context:NULL]; - - if (isDebugEnabled()) { - debugLog(@"Found Java Virtual Machines:"); - for (NSBundle *vm in vmBundles) { - debugLog([vm bundlePath]); - } - } - - NSString *requiredList = requiredJvmVersions(); - debugLog([NSString stringWithFormat:@"Required VMs: %@", requiredList]); - - if (requiredList != nil && requiredList != NULL) { - NSArray *array = [requiredList componentsSeparatedByString:@","]; - for (NSString* required in array) { - for (NSBundle *vm in vmBundles) { - if (satisfies(jvmVersion(vm), required)) { - debugLog(@"Chosen VM:"); - debugLog([vm bundlePath]); - return vm; - } - } - } - } - else { - NSLog(@"Info.plist is corrupted, Absent JVMOptions key."); - exit(-1); - } - NSLog(@"No matching VM found."); - showWarning(@"No matching VM found."); - return nil; -} - -CFBundleRef NSBundle2CFBundle(NSBundle *bundle) { - CFURLRef bundleURL = (CFURLRef) ([NSURL fileURLWithPath:bundle.bundlePath]); - return CFBundleCreate(kCFAllocatorDefault, bundleURL); -} - -- (NSString *)expandMacros:(NSString *)str { - return [[str - replaceAll:@"$APP_PACKAGE" to:[[NSBundle mainBundle] bundlePath]] - replaceAll:@"$USER_HOME" to:NSHomeDirectory()]; -} - -- (NSMutableString *)buildClasspath:(NSBundle *)jvm { - NSDictionary *jvmInfo = [[NSBundle mainBundle] objectForInfoDictionaryKey:JVMOptions]; - NSMutableString *classpathOption = [NSMutableString stringWithString:@"-Djava.class.path="]; - NSString *classPath = [jvmInfo objectForKey:@"ClassPath"]; - if (classPath != nil && classPath != NULL) { - [classpathOption appendString:[jvmInfo objectForKey:@"ClassPath"]]; - NSString *toolsJar = [[jvm bundlePath] stringByAppendingString:@"/Contents/Home/lib/tools.jar"]; - if ([[NSFileManager defaultManager] fileExistsAtPath:toolsJar]) { - [classpathOption appendString:@":"]; - [classpathOption appendString:toolsJar]; - } - } else { - NSLog(@"Info.plist is corrupted, Absent ClassPath key."); - exit(-1); - } - - return classpathOption; -} - -NSString *getJVMProperty(NSString *property) { - NSDictionary *jvmInfo = [[NSBundle mainBundle] objectForInfoDictionaryKey:JVMOptions]; - NSDictionary *properties = [jvmInfo dictionaryForKey:@"Properties"]; - if (properties != nil) { - return [properties objectForKey:property]; - } - return nil; -} - -NSString *getSelector() { - return getJVMProperty(@"idea.paths.selector"); -} - -NSString *getExecutable() { - return getJVMProperty(@"idea.executable"); -} - -NSString *getBundleName() { - return [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"]; -} - -NSString *getPropertiesFilePath() { - return [getPreferencesFolderPath() stringByAppendingString:@"/idea.properties"]; -} - - -NSString *getPreferencesFolderPath() { - return [NSString stringWithFormat:@"%@/Library/Preferences/%@", NSHomeDirectory(), getSelector()]; -} - -// NSString *getDefaultVMOptionsFilePath() { -// return [[[NSBundle mainBundle] bundlePath] stringByAppendingString:@fileName]; - -NSString *getDefaultFilePath(NSString *fileName) { - NSString *fullFileName = [[[NSBundle mainBundle] bundlePath] stringByAppendingString:@"/Contents"]; - fullFileName = [fullFileName stringByAppendingString:fileName]; - NSLog(@"fullFileName is: %@", fullFileName); - if ([[NSFileManager defaultManager] fileExistsAtPath:fullFileName]) { - NSLog(@"fullFileName exists: %@", fullFileName); - } else { - fullFileName = [[[NSBundle mainBundle] bundlePath] stringByAppendingString:fileName]; - NSLog(@"fullFileName exists: %@", fullFileName); - } - return fullFileName; -} - -NSString *getToolboxVMOptionsPath() { - return [NSString stringWithFormat:@"%@.vmoptions", [[NSBundle mainBundle] bundlePath]]; -} - -NSString *getApplicationVMOptionsPath() { - return getDefaultFilePath([NSString stringWithFormat:@"/bin/%@.vmoptions", getExecutable()]); -} - -NSString *getUserVMOptionsPath() { - return [NSString stringWithFormat:@"%@/%@.vmoptions", getPreferencesFolderPath(), getExecutable()]; -} - -NSString *getOverrideVMOptionsPath() { - NSString *variable = [[getExecutable() uppercaseString] stringByAppendingString:@"_VM_OPTIONS"]; - NSString *value = [[[NSProcessInfo processInfo] environment] objectForKey:variable]; - NSLog(@"Value of %@ is %@", variable, value); - return value == nil ? @"" : value; -} - -NSArray *parseVMOptions() { - NSString *vmOptionsFile = getOverrideVMOptionsPath(); - if (! [[NSFileManager defaultManager] fileExistsAtPath:vmOptionsFile]) { - vmOptionsFile = getToolboxVMOptionsPath(); - } - if (! [[NSFileManager defaultManager] fileExistsAtPath:vmOptionsFile]) { - vmOptionsFile = getUserVMOptionsPath(); - } - if (! [[NSFileManager defaultManager] fileExistsAtPath:vmOptionsFile]) { - vmOptionsFile = getApplicationVMOptionsPath(); - } - - NSMutableArray *options = [NSMutableArray array]; - - NSLog(@"Processing VMOptions file at %@", vmOptionsFile); - NSArray *contents = [VMOptionsReader readFile:vmOptionsFile]; - if (contents != nil) { - NSLog(@"Done"); - [options addObjectsFromArray:contents]; - [options addObject:[NSString stringWithFormat:@"-Djb.vmOptionsFile=%@", vmOptionsFile]]; - } else { - NSLog(@"No content found at %@", vmOptionsFile); - } - - return options; -} - -NSString *getOverridePropertiesPath() { - NSString *variable = [[getExecutable() uppercaseString] stringByAppendingString:@"_PROPERTIES"]; - return [[[NSProcessInfo processInfo] environment] objectForKey:variable]; -} - -- (void)fillArgs:(NSMutableArray *)args_array fromProperties:(NSDictionary *)properties { - if (properties != nil) { - for (id key in properties) { - [args_array addObject:[NSString stringWithFormat:@"-D%@=%@", key, [properties objectForKey:key]]]; - } - } -} - -- (JavaVMInitArgs)buildArgsFor:(NSBundle *)jvm { - NSMutableString *classpathOption = [self buildClasspath:jvm]; - - NSDictionary *jvmInfo = [[NSBundle mainBundle] objectForInfoDictionaryKey:JVMOptions]; - NSMutableArray *args_array = [NSMutableArray array]; - - [args_array addObject:classpathOption]; - [args_array addObjectsFromArray:parseVMOptions()]; - - NSString *properties = getOverridePropertiesPath(); - if (properties != nil) { - [args_array addObject:[NSString stringWithFormat:@"-Didea.properties.file=%@", properties]]; - } - - [self fillArgs:args_array fromProperties:[jvmInfo dictionaryForKey:@"Properties"]]; - - JavaVMInitArgs args; - args.version = JNI_VERSION_1_6; - args.ignoreUnrecognized = JNI_TRUE; - - args.nOptions = (jint)[args_array count]; - args.options = calloc((size_t) args.nOptions, sizeof(JavaVMOption)); - for (NSUInteger idx = 0; idx < args.nOptions; idx++) { - id obj = [args_array objectAtIndex:idx]; - args.options[idx].optionString = strdup([[self expandMacros:[obj description]] UTF8String]); - } - return args; -} - -- (const char *)mainClassName { - NSDictionary *jvmInfo = [[NSBundle mainBundle] objectForInfoDictionaryKey:JVMOptions]; - - NSString *mainClass = [jvmInfo objectForKey:@"MainClass"]; - if (mainClass == nil || mainClass == NULL) { - NSLog(@"Info.plist is corrupted, Absent MainClass key."); - exit(-1); - } - - char *answer = strdup([[jvmInfo objectForKey:@"MainClass"] UTF8String]); - - char *cur = answer; - while (*cur) { - if (*cur == '.') { - *cur = '/'; - } - cur++; - } - - return answer; -} - -- (void)process_cwd { - NSDictionary *jvmInfo = [[NSBundle mainBundle] objectForInfoDictionaryKey:JVMOptions]; - NSString *cwd = [jvmInfo objectForKey:@"WorkingDirectory"]; - if (cwd != nil && cwd != NULL) { - cwd = [self expandMacros:cwd]; - if (chdir([cwd UTF8String]) != 0) { - NSLog(@"Cannot chdir to working directory at %@", cwd); - } - } else { - NSString *dir = [[NSFileManager defaultManager] currentDirectoryPath]; - NSLog(@"WorkingDirectory is absent in Info.plist. Current Directory: %@", dir); - } -} - -BOOL validationJavaVersion(){ - vm = findMatchingVm(); - if (vm == nil) { - return false; - } - return true; -} - -- (void)alert:(NSArray *)values { - NSAlert *alert = [[[NSAlert alloc] init] autorelease]; - [alert setMessageText:[values objectAtIndex:0]]; - [alert setInformativeText:[values objectAtIndex:1]]; - - if ([values count] > 2) { - NSTextView *accessory = [[NSTextView alloc] initWithFrame:NSMakeRect(0, 0 , 300 , 15)]; - [accessory setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; - NSMutableAttributedString *str = [[NSMutableAttributedString alloc] initWithString: [values objectAtIndex:2]]; - [str addAttribute: NSLinkAttributeName value: [values objectAtIndex:2] range: NSMakeRange(0, str.length)]; - [accessory insertText:str]; - [accessory setEditable:NO]; - [accessory setDrawsBackground:NO]; - [alert setAccessoryView:accessory]; - } - - [alert runModal]; -} - -- (void)launch { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - if (vm == nil) { - showWarning(@"No matching VM found."); - NSLog(@"Cannot find matching VM, aborting"); - exit(-1); - } - - NSError *error = nil; - BOOL ok = [vm loadAndReturnError:&error]; - if (!ok) { - NSLog(@"Cannot load JVM bundle: %@", error); - exit(-1); - } - - CFBundleRef cfvm = NSBundle2CFBundle(vm); - - fun_ptr_t_CreateJavaVM create_vm = CFBundleGetFunctionPointerForName(cfvm, CFSTR("JNI_CreateJavaVM")); - - if (create_vm == NULL) { - // We have Apple VM chosen here... -/* - [self execCommandLineJava:vm]; - return; -*/ - - NSString *serverLibUrl = [vm.bundlePath stringByAppendingPathComponent:@"Contents/Libraries/libserver.dylib"]; - - void *libHandle = dlopen(serverLibUrl.UTF8String, RTLD_NOW + RTLD_GLOBAL); - if (libHandle) { - create_vm = dlsym(libHandle, "JNI_CreateJavaVM_Impl"); - } - } - - if (create_vm == NULL) { - NSLog(@"Cannot find JNI_CreateJavaVM in chosen JVM bundle at %@", vm.bundlePath); - exit(-1); - } - - [self process_cwd]; - - JNIEnv *env; - JavaVM *jvm; - - JavaVMInitArgs args = [self buildArgsFor:vm]; - - jint create_vm_rc = create_vm(&jvm, &env, &args); - if (create_vm_rc != JNI_OK || jvm == NULL) { - NSLog(@"JNI_CreateJavaVM (%@) failed: %ld", vm.bundlePath, create_vm_rc); - exit(-1); - } - - jclass string_class = (*env)->FindClass(env, "java/lang/String"); - if (string_class == NULL) { - NSLog(@"No java.lang.String in classpath!"); - exit(-1); - } - - const char *mainClassName = [self mainClassName]; - jclass mainClass = (*env)->FindClass(env, mainClassName); - if (mainClass == NULL || (*env)->ExceptionOccurred(env)) { - NSLog(@"Main class %s not found", mainClassName); - (*env)->ExceptionDescribe(env); - exit(-1); - } - - jmethodID mainMethod = (*env)->GetStaticMethodID(env, mainClass, "main", "([Ljava/lang/String;)V"); - if (mainMethod == NULL || (*env)->ExceptionOccurred(env)) { - NSLog(@"Cant't find main() method"); - (*env)->ExceptionDescribe(env); - exit(-1); - } - - // See http://stackoverflow.com/questions/10242115/os-x-strange-psn-command-line-parameter-when-launched-from-finder - // about psn_ stuff - int arg_count = 0; - for (int i = 1; i < argc; i++) { - if (memcmp(argv[i], "-psn_", 4) != 0) arg_count++; - } - - jobject jni_args = (*env)->NewObjectArray(env, arg_count, string_class, NULL); - - arg_count = 0; - for (int i = 1; i < argc; i++) { - if (memcmp(argv[i], "-psn_", 4) != 0) { - jstring jni_arg = (*env)->NewStringUTF(env, argv[i]); - (*env)->SetObjectArrayElement(env, jni_args, arg_count, jni_arg); - arg_count++; - } - } - - (*env)->CallStaticVoidMethod(env, mainClass, mainMethod, jni_args); - - (*jvm)->DetachCurrentThread(jvm); - (*jvm)->DestroyJavaVM(jvm); - - [pool release]; -} - +// +// Created by max on 5/4/12. +// +// To change the template use AppCode | Preferences | File Templates. +// + + +#import "Launcher.h" +#import "VMOptionsReader.h" +#import "PropertyFileReader.h" +#import "utils.h" +#import +@class NSAlert; + +typedef jint (JNICALL *fun_ptr_t_CreateJavaVM)(JavaVM **pvm, void **env, void *args); +NSBundle *vm; +NSString *const JVMOptions = @"JVMOptions"; +NSString *JVMVersion = NULL; +NSString* minRequiredJavaVersion = @"1.8"; +NSString* osxVersion = @"10.10"; +BOOL javaUpdateRequired = false; + + +@interface NSString (CustomReplacements) +- (NSString *)replaceAll:(NSString *)pattern to:(NSString *)replacement; + +@end + +@implementation NSString (CustomReplacements) +- (NSString *)replaceAll:(NSString *)pattern to:(NSString *)replacement { + if ([self rangeOfString:pattern].length == 0) return self; + + NSMutableString *answer = [[self mutableCopy] autorelease]; + [answer replaceOccurrencesOfString:pattern withString:replacement options:0 range:NSMakeRange(0, [self length])]; + return answer; +} +@end + +@interface NSDictionary (TypedGetters) +- (NSDictionary *)dictionaryForKey:(id)key; +- (id)valueForKey:(NSString *)key inDictionary:(NSString *)dictKey defaultObject:(NSString *)defaultValue; +@end + +@implementation NSDictionary (TypedGetters) +- (NSDictionary *)dictionaryForKey:(id)key { + id answer = [self objectForKey:key]; + if ([answer isKindOfClass:[NSDictionary class]]) { + return answer; + } + return nil; +} + +- (id)valueForKey:(NSString *)key inDictionary:(NSString *)dictKey defaultObject: (NSString*) defaultValue { + NSDictionary *dict = [self dictionaryForKey:dictKey]; + if (dict == nil) return nil; + id answer = [dict valueForKey:key]; + return answer != nil ? answer : defaultValue; +} +@end + +@implementation Launcher + +- (id)initWithArgc:(int)anArgc argv:(char **)anArgv { + self = [super init]; + if (self) { + argc = anArgc; + argv = anArgv; + } + + return self; +} + +NSString* getOSXVersion(){ + NSString *versionString; + NSDictionary * sv = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]; + versionString = [sv objectForKey:@"ProductVersion"]; + //NSLog(@"OS X: %@", versionString); + return versionString; +} + +void showWarning(NSString* messageText){ + NSAlert* alert = [[NSAlert alloc] init]; + [alert addButtonWithTitle:@"OK"]; + NSString* message_description = [NSString stringWithFormat:@"Java 1.8 or later is required."]; + NSString* informativeText =[NSString stringWithFormat:@"%@",message_description]; + [alert setMessageText:messageText]; + [alert setInformativeText:informativeText ]; + [alert setAlertStyle:NSWarningAlertStyle]; + [alert runModal]; + [alert release]; +} + + +BOOL appendBundle(NSString *path, NSMutableArray *sink) { + if ([path hasSuffix:@"jdk"] || [path hasSuffix:@".jre"] || [path hasSuffix:@"jbr"]) { + NSBundle *bundle = [NSBundle bundleWithPath:path]; + if (bundle != nil) { + [sink addObject:bundle]; + return true; + } + } + return false; +} + +BOOL appendJvmBundlesAt(NSString *path, NSMutableArray *sink) { + NSError *error = nil; + NSArray *names = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:&error]; + + BOOL res = false; + if (names != nil) { + for (NSString *name in names) { + res |= appendBundle([path stringByAppendingPathComponent:name], sink); + } + } + return res; +} + +NSArray *allVms() { + NSMutableArray *jvmBundlePaths = [NSMutableArray array]; + + // search java info in user's idea.properties + NSString* ideaProperty = getPropertiesFilePath(); + if ([[NSFileManager defaultManager] fileExistsAtPath:ideaProperty]) { + NSDictionary *inConfig =[PropertyFileReader readFile:ideaProperty]; + NSString* userJavaVersion =[inConfig objectForKey:@"JVMVersion"]; + if (userJavaVersion != nil && meetMinRequirements(userJavaVersion)) { + JVMVersion = userJavaVersion; + } + } + NSString *required = requiredJvmVersions(); + NSLog(@"allVms required %@", required); + + if (! jvmBundlePaths.count > 0 ) { + NSBundle *bundle = [NSBundle mainBundle]; + NSString *appDir = [bundle.bundlePath stringByAppendingPathComponent:@"Contents"]; + + if (!appendJvmBundlesAt([appDir stringByAppendingPathComponent:@"/jre"], jvmBundlePaths)) { + if (! appendBundle([appDir stringByAppendingPathComponent:@"/jdk"], jvmBundlePaths)) { + appendBundle([appDir stringByAppendingPathComponent:@"/jbr"], jvmBundlePaths); + } + } + if ((jvmBundlePaths.count > 0) && (satisfies(jvmVersion(jvmBundlePaths[jvmBundlePaths.count-1]), required))) return jvmBundlePaths; + + appendJvmBundlesAt([NSHomeDirectory() stringByAppendingPathComponent:@"Library/Java/JavaVirtualMachines"], jvmBundlePaths); + appendJvmBundlesAt(@"/Library/Java/JavaVirtualMachines", jvmBundlePaths); + appendJvmBundlesAt(@"/System/Library/Java/JavaVirtualMachines", jvmBundlePaths); + } + + return jvmBundlePaths; +} + +NSString *jvmVersion(NSBundle *bundle) { + NSString* javaVersion = [bundle.infoDictionary valueForKey:@"JVMVersion" inDictionary:@"JavaVM" defaultObject:@"0"]; + //NSLog(@"jvmVersion: %@", javaVersion); + return javaVersion; +} + +NSString *requiredJvmVersions() { + return (JVMVersion != NULL) ? JVMVersion : [[NSBundle mainBundle].infoDictionary valueForKey:@"JVMVersion" inDictionary: JVMOptions defaultObject:@"1.8*"]; +} + +BOOL meetMinRequirements (NSString *vmVersion) { + return [minRequiredJavaVersion compare:vmVersion options:NSNumericSearch] <= 0; +} + +BOOL satisfies(NSString *vmVersion, NSString *requiredVersion) { + BOOL meetRequirement = meetMinRequirements(vmVersion); + if (! meetRequirement) { + return meetRequirement; + } + + if ([requiredVersion hasSuffix:@"+"]) { + requiredVersion = [requiredVersion substringToIndex:[requiredVersion length] - 1]; + return [requiredVersion compare:vmVersion options:NSNumericSearch] <= 0; + } + if ([requiredVersion hasSuffix:@"*"]) { + requiredVersion = [requiredVersion substringToIndex:[requiredVersion length] - 1]; + } + return [vmVersion hasPrefix:requiredVersion]; +} + +NSComparisonResult compareVMVersions(id vm1, id vm2, void *context) { + return [jvmVersion(vm2) compare:jvmVersion(vm1) options:NSNumericSearch]; +} + +NSBundle *getJDKBundle(NSString* jdkVersion, NSString* source) { + if (jdkVersion != nil) { + NSBundle *jdkBundle = [NSBundle bundleWithPath : jdkVersion]; + if (jdkBundle != nil && ![jvmVersion(jdkBundle) isEqualToString :@"0"]) { + NSString *javaVersion = jvmVersion(jdkBundle); + if (javaVersion != nil && meetMinRequirements(javaVersion)) { + debugLog(@"VM from:"); + debugLog(source); + debugLog([jdkBundle bundlePath]); + return jdkBundle; + } + } + } + return nil; +} + +NSBundle *findMatchingVm() { + // boot jdk action + NSFileManager *fileManager = [NSFileManager defaultManager]; + + NSString *pathForFile = [NSString stringWithFormat:@"%@/%@.jdk", getPreferencesFolderPath(), getExecutable()]; + + if (!pathForFile.isAbsolutePath) { + // Handle relative paths + pathForFile = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:pathForFile]; + } + + if ([fileManager fileExistsAtPath:pathForFile]) { + NSString* fileContents = [NSString stringWithContentsOfFile:pathForFile encoding:NSUTF8StringEncoding error:nil]; + NSArray* allLinedStrings = [fileContents componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]]; + if (allLinedStrings.count > 0) { + NSString* jdkFromProfile = allLinedStrings[0]; + NSBundle *jdkBundle = getJDKBundle(jdkFromProfile, @"IDE profile"); + if (jdkBundle != nil) { + return jdkBundle; + } + } + } + + //the environment variable. + NSString *variable = [[getExecutable() uppercaseString] stringByAppendingString:@"_JDK"]; + // The explicitly set JDK to use. + NSString *explicit = [[[NSProcessInfo processInfo] environment] objectForKey:variable]; + if (explicit != nil) { + NSLog(@"Value of %@: %@", variable, explicit); + NSBundle *jdkBundle = getJDKBundle(explicit, @"environment variable"); + if (jdkBundle != nil) { + return jdkBundle; + } + } + + NSArray *vmBundles = [allVms() sortedArrayUsingFunction:compareVMVersions context:NULL]; + + if (isDebugEnabled()) { + debugLog(@"Found Java Virtual Machines:"); + for (NSBundle *vm in vmBundles) { + debugLog([vm bundlePath]); + } + } + + NSString *requiredList = requiredJvmVersions(); + debugLog([NSString stringWithFormat:@"Required VMs: %@", requiredList]); + + if (requiredList != nil && requiredList != NULL) { + NSArray *array = [requiredList componentsSeparatedByString:@","]; + for (NSString* required in array) { + for (NSBundle *vm in vmBundles) { + if (satisfies(jvmVersion(vm), required)) { + debugLog(@"Chosen VM:"); + debugLog([vm bundlePath]); + return vm; + } + } + } + } + else { + NSLog(@"Info.plist is corrupted, Absent JVMOptions key."); + exit(-1); + } + NSLog(@"No matching VM found."); + showWarning(@"No matching VM found."); + return nil; +} + +CFBundleRef NSBundle2CFBundle(NSBundle *bundle) { + CFURLRef bundleURL = (CFURLRef) ([NSURL fileURLWithPath:bundle.bundlePath]); + return CFBundleCreate(kCFAllocatorDefault, bundleURL); +} + +- (NSString *)expandMacros:(NSString *)str { + return [[str + replaceAll:@"$APP_PACKAGE" to:[[NSBundle mainBundle] bundlePath]] + replaceAll:@"$USER_HOME" to:NSHomeDirectory()]; +} + +- (NSMutableString *)buildClasspath:(NSBundle *)jvm { + NSDictionary *jvmInfo = [[NSBundle mainBundle] objectForInfoDictionaryKey:JVMOptions]; + NSMutableString *classpathOption = [NSMutableString stringWithString:@"-Djava.class.path="]; + NSString *classPath = [jvmInfo objectForKey:@"ClassPath"]; + if (classPath != nil && classPath != NULL) { + [classpathOption appendString:[jvmInfo objectForKey:@"ClassPath"]]; + NSString *toolsJar = [[jvm bundlePath] stringByAppendingString:@"/Contents/Home/lib/tools.jar"]; + if ([[NSFileManager defaultManager] fileExistsAtPath:toolsJar]) { + [classpathOption appendString:@":"]; + [classpathOption appendString:toolsJar]; + } + } else { + NSLog(@"Info.plist is corrupted, Absent ClassPath key."); + exit(-1); + } + + return classpathOption; +} + +NSString *getJVMProperty(NSString *property) { + NSDictionary *jvmInfo = [[NSBundle mainBundle] objectForInfoDictionaryKey:JVMOptions]; + NSDictionary *properties = [jvmInfo dictionaryForKey:@"Properties"]; + if (properties != nil) { + return [properties objectForKey:property]; + } + return nil; +} + +NSString *getSelector() { + return getJVMProperty(@"idea.paths.selector"); +} + +NSString *getExecutable() { + return getJVMProperty(@"idea.executable"); +} + +NSString *getBundleName() { + return [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"]; +} + +NSString *getPropertiesFilePath() { + return [getPreferencesFolderPath() stringByAppendingString:@"/idea.properties"]; +} + + +NSString *getPreferencesFolderPath() { + return [NSString stringWithFormat:@"%@/Library/Preferences/%@", NSHomeDirectory(), getSelector()]; +} + +// NSString *getDefaultVMOptionsFilePath() { +// return [[[NSBundle mainBundle] bundlePath] stringByAppendingString:@fileName]; + +NSString *getDefaultFilePath(NSString *fileName) { + NSString *fullFileName = [[[NSBundle mainBundle] bundlePath] stringByAppendingString:@"/Contents"]; + fullFileName = [fullFileName stringByAppendingString:fileName]; + NSLog(@"fullFileName is: %@", fullFileName); + if ([[NSFileManager defaultManager] fileExistsAtPath:fullFileName]) { + NSLog(@"fullFileName exists: %@", fullFileName); + } else { + fullFileName = [[[NSBundle mainBundle] bundlePath] stringByAppendingString:fileName]; + NSLog(@"fullFileName exists: %@", fullFileName); + } + return fullFileName; +} + +NSString *getToolboxVMOptionsPath() { + return [NSString stringWithFormat:@"%@.vmoptions", [[NSBundle mainBundle] bundlePath]]; +} + +NSString *getApplicationVMOptionsPath() { + return getDefaultFilePath([NSString stringWithFormat:@"/bin/%@.vmoptions", getExecutable()]); +} + +NSString *getUserVMOptionsPath() { + return [NSString stringWithFormat:@"%@/%@.vmoptions", getPreferencesFolderPath(), getExecutable()]; +} + +NSString *getOverrideVMOptionsPath() { + NSString *variable = [[getExecutable() uppercaseString] stringByAppendingString:@"_VM_OPTIONS"]; + NSString *value = [[[NSProcessInfo processInfo] environment] objectForKey:variable]; + NSLog(@"Value of %@ is %@", variable, value); + return value == nil ? @"" : value; +} + +NSArray *parseVMOptions() { + NSString *vmOptionsFile = getOverrideVMOptionsPath(); + if (! [[NSFileManager defaultManager] fileExistsAtPath:vmOptionsFile]) { + vmOptionsFile = getToolboxVMOptionsPath(); + } + if (! [[NSFileManager defaultManager] fileExistsAtPath:vmOptionsFile]) { + vmOptionsFile = getUserVMOptionsPath(); + } + if (! [[NSFileManager defaultManager] fileExistsAtPath:vmOptionsFile]) { + vmOptionsFile = getApplicationVMOptionsPath(); + } + + NSMutableArray *options = [NSMutableArray array]; + + NSLog(@"Processing VMOptions file at %@", vmOptionsFile); + NSArray *contents = [VMOptionsReader readFile:vmOptionsFile]; + if (contents != nil) { + NSLog(@"Done"); + [options addObjectsFromArray:contents]; + [options addObject:[NSString stringWithFormat:@"-Djb.vmOptionsFile=%@", vmOptionsFile]]; + } else { + NSLog(@"No content found at %@", vmOptionsFile); + } + + return options; +} + +NSString *getOverridePropertiesPath() { + NSString *variable = [[getExecutable() uppercaseString] stringByAppendingString:@"_PROPERTIES"]; + return [[[NSProcessInfo processInfo] environment] objectForKey:variable]; +} + +- (void)fillArgs:(NSMutableArray *)args_array fromProperties:(NSDictionary *)properties { + if (properties != nil) { + for (id key in properties) { + [args_array addObject:[NSString stringWithFormat:@"-D%@=%@", key, [properties objectForKey:key]]]; + } + } +} + +- (JavaVMInitArgs)buildArgsFor:(NSBundle *)jvm { + NSMutableString *classpathOption = [self buildClasspath:jvm]; + + NSDictionary *jvmInfo = [[NSBundle mainBundle] objectForInfoDictionaryKey:JVMOptions]; + NSMutableArray *args_array = [NSMutableArray array]; + + [args_array addObject:classpathOption]; + [args_array addObjectsFromArray:parseVMOptions()]; + + NSString *properties = getOverridePropertiesPath(); + if (properties != nil) { + [args_array addObject:[NSString stringWithFormat:@"-Didea.properties.file=%@", properties]]; + } + + [self fillArgs:args_array fromProperties:[jvmInfo dictionaryForKey:@"Properties"]]; + + JavaVMInitArgs args; + args.version = JNI_VERSION_1_6; + args.ignoreUnrecognized = JNI_TRUE; + + args.nOptions = (jint)[args_array count]; + args.options = calloc((size_t) args.nOptions, sizeof(JavaVMOption)); + for (NSUInteger idx = 0; idx < args.nOptions; idx++) { + id obj = [args_array objectAtIndex:idx]; + args.options[idx].optionString = strdup([[self expandMacros:[obj description]] UTF8String]); + } + return args; +} + +- (const char *)mainClassName { + NSDictionary *jvmInfo = [[NSBundle mainBundle] objectForInfoDictionaryKey:JVMOptions]; + + NSString *mainClass = [jvmInfo objectForKey:@"MainClass"]; + if (mainClass == nil || mainClass == NULL) { + NSLog(@"Info.plist is corrupted, Absent MainClass key."); + exit(-1); + } + + char *answer = strdup([[jvmInfo objectForKey:@"MainClass"] UTF8String]); + + char *cur = answer; + while (*cur) { + if (*cur == '.') { + *cur = '/'; + } + cur++; + } + + return answer; +} + +- (void)process_cwd { + NSDictionary *jvmInfo = [[NSBundle mainBundle] objectForInfoDictionaryKey:JVMOptions]; + NSString *cwd = [jvmInfo objectForKey:@"WorkingDirectory"]; + if (cwd != nil && cwd != NULL) { + cwd = [self expandMacros:cwd]; + if (chdir([cwd UTF8String]) != 0) { + NSLog(@"Cannot chdir to working directory at %@", cwd); + } + } else { + NSString *dir = [[NSFileManager defaultManager] currentDirectoryPath]; + NSLog(@"WorkingDirectory is absent in Info.plist. Current Directory: %@", dir); + } +} + +BOOL validationJavaVersion(){ + vm = findMatchingVm(); + if (vm == nil) { + return false; + } + return true; +} + +- (void)alert:(NSArray *)values { + NSAlert *alert = [[[NSAlert alloc] init] autorelease]; + [alert setMessageText:[values objectAtIndex:0]]; + [alert setInformativeText:[values objectAtIndex:1]]; + + if ([values count] > 2) { + NSTextView *accessory = [[NSTextView alloc] initWithFrame:NSMakeRect(0, 0 , 300 , 15)]; + [accessory setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; + NSMutableAttributedString *str = [[NSMutableAttributedString alloc] initWithString: [values objectAtIndex:2]]; + [str addAttribute: NSLinkAttributeName value: [values objectAtIndex:2] range: NSMakeRange(0, str.length)]; + [accessory insertText:str]; + [accessory setEditable:NO]; + [accessory setDrawsBackground:NO]; + [alert setAccessoryView:accessory]; + } + + [alert runModal]; +} + +- (void)launch { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + if (vm == nil) { + showWarning(@"No matching VM found."); + NSLog(@"Cannot find matching VM, aborting"); + exit(-1); + } + + NSError *error = nil; + BOOL ok = [vm loadAndReturnError:&error]; + if (!ok) { + NSLog(@"Cannot load JVM bundle: %@", error); + exit(-1); + } + + CFBundleRef cfvm = NSBundle2CFBundle(vm); + + fun_ptr_t_CreateJavaVM create_vm = CFBundleGetFunctionPointerForName(cfvm, CFSTR("JNI_CreateJavaVM")); + + if (create_vm == NULL) { + // We have Apple VM chosen here... +/* + [self execCommandLineJava:vm]; + return; +*/ + + NSString *serverLibUrl = [vm.bundlePath stringByAppendingPathComponent:@"Contents/Libraries/libserver.dylib"]; + + void *libHandle = dlopen(serverLibUrl.UTF8String, RTLD_NOW + RTLD_GLOBAL); + if (libHandle) { + create_vm = dlsym(libHandle, "JNI_CreateJavaVM_Impl"); + } + } + + if (create_vm == NULL) { + NSLog(@"Cannot find JNI_CreateJavaVM in chosen JVM bundle at %@", vm.bundlePath); + exit(-1); + } + + [self process_cwd]; + + JNIEnv *env; + JavaVM *jvm; + + JavaVMInitArgs args = [self buildArgsFor:vm]; + + jint create_vm_rc = create_vm(&jvm, &env, &args); + if (create_vm_rc != JNI_OK || jvm == NULL) { + NSLog(@"JNI_CreateJavaVM (%@) failed: %ld", vm.bundlePath, create_vm_rc); + exit(-1); + } + + jclass string_class = (*env)->FindClass(env, "java/lang/String"); + if (string_class == NULL) { + NSLog(@"No java.lang.String in classpath!"); + exit(-1); + } + + const char *mainClassName = [self mainClassName]; + jclass mainClass = (*env)->FindClass(env, mainClassName); + if (mainClass == NULL || (*env)->ExceptionOccurred(env)) { + NSLog(@"Main class %s not found", mainClassName); + (*env)->ExceptionDescribe(env); + exit(-1); + } + + jmethodID mainMethod = (*env)->GetStaticMethodID(env, mainClass, "main", "([Ljava/lang/String;)V"); + if (mainMethod == NULL || (*env)->ExceptionOccurred(env)) { + NSLog(@"Cant't find main() method"); + (*env)->ExceptionDescribe(env); + exit(-1); + } + + // See http://stackoverflow.com/questions/10242115/os-x-strange-psn-command-line-parameter-when-launched-from-finder + // about psn_ stuff + int arg_count = 0; + for (int i = 1; i < argc; i++) { + if (memcmp(argv[i], "-psn_", 4) != 0) arg_count++; + } + + jobject jni_args = (*env)->NewObjectArray(env, arg_count, string_class, NULL); + + arg_count = 0; + for (int i = 1; i < argc; i++) { + if (memcmp(argv[i], "-psn_", 4) != 0) { + jstring jni_arg = (*env)->NewStringUTF(env, argv[i]); + (*env)->SetObjectArrayElement(env, jni_args, arg_count, jni_arg); + arg_count++; + } + } + + (*env)->CallStaticVoidMethod(env, mainClass, mainMethod, jni_args); + + (*jvm)->DetachCurrentThread(jvm); + (*jvm)->DestroyJavaVM(jvm); + + [pool release]; +} + @end \ No newline at end of file diff --git a/platform/build-scripts/resources/mac/Contents/MacOS/executable b/platform/build-scripts/resources/mac/Contents/MacOS/executable index d219833299920011058184fc68d7bebe069ab40b..1370914d0071546faf2393ce14d67b18b29ecc49 100644 GIT binary patch delta 24146 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 I9&Yjf05vf(jQ{`u delta 23891 zcmZvE349dA^8ZW@NFX7bgv|lD4|0(USQ3tq1PBBOun|eZ6;1_F@B;Aw7lZ)oxNaOmdNhiO) z==XimE8egXwtS=QcZUDGaX86X^2-RH|8SaAELiiC&QCX*LIFL%!gR zlV}4w2=tspr!*7rITjyM7(B30@Rmz-AbT9>nw3X8B-nG8Nco@j4kxDni({S+2y9Wz?Ae!xpG5be#* ze;Cd>g(q2a#!16v>qLvz=b?7DtTkIFTGSpv?EzV9T26FR)7cS`)^n2KhEz^8XV_R9 zXFV`pwA>^Onpf_Qi1Ru(zV`cw&Z^~|=ZaFZt`{VHl6SqTYD!WV%W0FU-mV?iX0X?i zB0F0f)yC0gsG^i<1-zhxX3HnY*=QW@=iHUspf`(Alz9z7^Sm(K zO0|OT@BfQ4OmOygeqZK1ZyYYuk^=-%=Db4UJHh|?U;Kgh%1MRIK*oxr;~f5&yfqEe zBTKcRyGv^hO|zi#81XpI8rJHbocS}89>i(iJf7v3x&mjwJwFFoe0M@iQ01Q7Te*iy zVDHmuiqg;7s82v%ss#pd{z!BP{VR2z)cXK0a~gVnT8g6N-a=cXWDb-7Qk1#3)#+DZ zM-JL8bN*&nj}^1#F`di9K^xZP#ZU@MCxRo8IJC_91Ub(Z8{YuiZYQD2sxur z8>rvM-i(b%=|MxL+0GKL%bG*I(V~OV1fLA61l*;VJ(kjg1y{#1rLj=0955RV0^gDcGSOzdRPei8Z6EL1NyGXDs>q??02=^eqLey6H?03=rSbXd2sS6a zZR8>v8O=6^sx@0bNQlv_WLx5UM8uIQm&+$r06$9SQ6d|iCqd6^m{tYd-wLMvyy~PwFh&yP zvOYmBrCGxj)|{G_3ISi?!=qr?hLHgSoNtFsaK58?sWbXlSgU{IMZMfr>M9rrB{Y$4 z>CQwjX|_ioS?2u7u(lOKK{Z(_RTOj^q&SG-elQ3-?jt+60hrBJB3#xOVHB1#X_w2I z!6D2L0U`2!)KmU63{0Q|NVFH#(0phBEf{OMajnEP^A8NB^Cv{tF;ta09cK*de5%Go zs-g<#8EOS{0@C}y0!)~;oAYzuz{Nzkti5RtxB?5%UYX0cLKw=egh{vb7vU|KRHDyJ z;Otw>DP!vfdBYmmP06J0l3|l!7@SuN79vLo@(W3pakF){-4+^E>bwo#O%TIqgCaR9 znED^EsmY0>Gf;(T^GAPKg+Ss<)016!KcDx>ZMZfZ{v81ZbmBv96*&{b3KqBTvkMo(T(M2%LIr1ma3Rx^gQzLeB4(I zEUqpdJ<3Ra4?+|P6ahA0640Vw1;&@aKcN`5K2Jl1U1t(z5d5D~Q*6CUNj-+U8#iW0DFW4wp;`tU zGdMI`4Y9kd&1fK9frhX&LYO$vsf*b?n`NfvIO@@D@NDEfzI#Z5n;H%k#0Rhb2Xil9 zxYHjfx0D+*7YAHg5msOp2SK|Y7(;#dP4`8P?tYA8CcI!+hZ7?<#2=y+oQWa1O}c1RqJsce zOaqEFRU8&4mPvVBEk2=~sxdT4Gt{n0dB6pV1uA81B~ z*=o2d%tPNDBYqiCR@+jp^{oCKRx{JIjoz@S+ifgT~x%sbNipapDM^ z10CepUF29-!9><7BPqNEHSW2CW26o15!NpwN)2KUW~8exu_YO?cy7uF9Z}VZOeGVi zFV)ZAMVs994^eMi*3F0;jE-hgdlTCUG|u@O^^e;Cs5uohZ#_0SFx(&pmvyxWG`P^{ z$YR^4c-`v7O54|~(MvN^f=2PZxyFK%0AjL|bV9KaZsIqy;rp~tWmuydLB57%=ckaa*jdC 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 UMwZv2yj+(5UH^8D`gQC72RmlvlK=n!