diff --git a/.gitignore b/.gitignore index 6d3f90f..3559f19 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,112 @@ + +# Created by https://www.gitignore.io/api/gradle,intellij+all +# Edit at https://www.gitignore.io/?templates=gradle,intellij+all + +### Intellij+all ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### Intellij+all Patch ### +# Ignores the whole .idea folder and all .iml files +# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 + +.idea/ + +# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 + +*.iml +modules.xml +.idea/misc.xml +*.ipr + +# Sonarlint plugin +.idea/sonarlint + +### Gradle ### .gradle -build +build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Cache of project +.gradletasknamecache + +# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 +# gradle/wrapper/gradle-wrapper.properties + +### Gradle Patch ### +**/build/ + +# End of https://www.gitignore.io/api/gradle,intellij+all +gradle JavaAppLauncher diff --git a/build.gradle b/build.gradle index 7b5443d..5b74f84 100644 --- a/build.gradle +++ b/build.gradle @@ -1,11 +1,15 @@ +import org.gradle.internal.jvm.Jvm + buildscript { repositories { jcenter() mavenCentral() + mavenLocal() } + dependencies { classpath 'org.codehaus.groovy:groovy-all:2.3.6' - classpath 'nu.studer:gradle-plugindev-plugin:1.0.3' + classpath 'nu.studer:gradle-plugindev-plugin:1.0.12' } } @@ -17,19 +21,18 @@ apply plugin: 'nu.studer.plugindev' group = 'com.github.cr0' // find osx sdk -def osxSDKRoot = new File("/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/") -if (!osxSDKRoot.exists()) throw new GradleException("SDK root does not exist") -def sdk = null -for (def c : osxSDKRoot.listFiles()) { - if (c.name.compareTo("MacOSX10.8.sdk") < 0) continue // fancy ;) - - if (sdk == null) sdk = c.name - else if (sdk.compareTo(c.name) < 0) sdk = c.name +String findMacOSSDKRoot() { + def stdoutBuffer = new ByteArrayOutputStream() + exec { + commandLine "xcrun", "--sdk", "macosx", "--show-sdk-path" + standardOutput = stdoutBuffer + } + return stdoutBuffer.toString().trim() } -if (sdk == null) throw new GradleException("No feasible SDK found") -def osxSDK = new File(osxSDKRoot, sdk) +def osxSDK = findMacOSSDKRoot() +if (osxSDK == "") throw new GradleException("No feasible SDK found") model { platforms { @@ -41,21 +44,34 @@ model { buildTypes { release } -} -executables { - main { - baseName "JavaAppLauncher" + repositories { + libs(PrebuiltLibraries) { + javaHeaders { + headers.srcDirs "${Jvm.current().javaHome}/include", "${Jvm.current().javaHome}/include/darwin" + } + } + } + + components { + JavaAppLauncher(NativeExecutableSpec) { + sources { + objc { + lib library: 'javaHeaders', linkage: 'api' + source { + srcDirs "src/main/objc" + include "**/*.m" + } + } + } + + } } - all { - binaries.all { - if (targetPlatform.operatingSystem.macOsX) { - objcCompiler.args '-I', "${org.gradle.internal.jvm.Jvm.current().javaHome}/include" - objcCompiler.args '-I', "${org.gradle.internal.jvm.Jvm.current().javaHome}/include/darwin" - objcCompiler.args '-F', "${org.gradle.internal.jvm.Jvm.current().javaHome}/../../" + binaries { + all { + if (toolChain in Clang) { linker.args '-framework', "Cocoa" - linker.args '-isysroot', osxSDK.absolutePath } } } @@ -82,8 +98,8 @@ bintray { pkg.repo = 'gradle-plugins' } -task copyStub(type: Copy, dependsOn: 'mainExecutable') { - from 'build/binaries/mainExecutable/' +task copyStub(type: Copy, dependsOn: 'JavaAppLauncherExecutable') { + from 'build/exe/javaAppLauncher/' into 'src/main/resources/com/github/cr0/macAppBundle/' outputs.upToDateWhen { false } } diff --git a/gradle.properties b/gradle.properties index d2757ca..be9eda0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version = 3.1.0 \ No newline at end of file +version = 3.1.2 \ No newline at end of file diff --git a/src/main/groovy/com/github/cr0/gradle/macAppBundle/MacAppBundlePlugin.groovy b/src/main/groovy/com/github/cr0/gradle/macAppBundle/MacAppBundlePlugin.groovy index b91a087..eb1ed4d 100644 --- a/src/main/groovy/com/github/cr0/gradle/macAppBundle/MacAppBundlePlugin.groovy +++ b/src/main/groovy/com/github/cr0/gradle/macAppBundle/MacAppBundlePlugin.groovy @@ -241,7 +241,7 @@ class MacAppBundlePlugin implements Plugin<Project> { task.description = "Create a dmg containing the .app and optional background image" task.group = GROUP task.inputs.dir("${-> project.buildDir}/${-> project.macAppBundle.appOutputDir}") - task.inputs.property("backgroundImage", { project.macAppBundle.backgroundImage }) + task.inputs.property("backgroundImage", { project.macAppBundle.backgroundImage }).optional(true) task.outputs.file("${-> project.buildDir}/${-> project.macAppBundle.archiveOutputDir}/${-> project.macAppBundle.archiveName}.dmg") project.afterEvaluate { diff --git a/src/main/objc/main.m b/src/main/objc/main.m index b0ce1bb..03b9938 100644 --- a/src/main/objc/main.m +++ b/src/main/objc/main.m @@ -27,6 +27,7 @@ #import <Cocoa/Cocoa.h> #include <dlfcn.h> #include <jni.h> +#include <fts.h> #define JAVA_LAUNCH_ERROR "JavaLaunchError" @@ -67,8 +68,9 @@ typedef int (JNICALL *JLI_Launch_t)(int argc, char ** argv, int launch(char *, int, char **); NSString * findDylib (bool); -int extractMajorVersion (NSString *vstring) -;NSString * convertRelativeFilePath(NSString * path); +int extractMajorVersion (NSString *vstring); +NSString * convertRelativeFilePath(NSString * path); +NSString * findFirstFile(NSString * needle, NSString * haystack); int main(int argc, char *argv[]) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; @@ -84,7 +86,7 @@ int main(int argc, char *argv[]) { result = 0; } @catch (NSException *exception) { NSAlert *alert = [[NSAlert alloc] init]; - [alert setAlertStyle:NSCriticalAlertStyle]; + [alert setAlertStyle:NSAlertStyleCritical]; [alert setMessageText:[exception reason]]; [alert runModal]; @@ -107,7 +109,7 @@ int launch(char *commandName, int progargc, char *progargv[]) { NSDictionary *infoDictionary = [mainBundle infoDictionary]; // Test for debugging (but only on the second runthrough) - bool isDebugging = (launchCount > 0) && [[infoDictionary objectForKey:@JVM_DEBUG_KEY] boolValue]; + bool isDebugging = [[infoDictionary objectForKey:@JVM_DEBUG_KEY] boolValue]; if (isDebugging) { NSLog(@"Loading Application '%@'", [infoDictionary objectForKey:@"CFBundleName"]); @@ -464,7 +466,7 @@ int launch(char *commandName, int progargc, char *progargv[]) { NSTask *task = [[NSTask alloc] init]; [task setLaunchPath:@"/usr/libexec/java_home"]; - NSArray *args = [NSArray arrayWithObjects: @"-v", @"1.7+", nil]; + NSArray *args = [NSArray arrayWithObjects: @"-v", @"1.7+", @"-F", nil]; [task setArguments:args]; NSPipe *stdout = [NSPipe pipe]; @@ -499,31 +501,9 @@ int launch(char *commandName, int progargc, char *progargv[]) { return nil; } - int version = 0; - - NSRange vrange = [outRead rangeOfString:@"jdk1."]; - - if (vrange.location != NSNotFound) { - NSString *vstring = [outRead substringFromIndex:(vrange.location)]; - - vrange = [vstring rangeOfString:@"/"]; - vstring = [vstring substringToIndex:vrange.location]; - - version = extractMajorVersion(vstring); - - if (isDebugging) { - NSLog (@"Found a Java %@ JDK", vstring); - NSLog (@"Looks like major version %d", extractMajorVersion(vstring)); - } - } - - if ( version >= 7 ) { - if (isDebugging) { - NSLog (@"JDK version qualifies"); - } - return [[outRead stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] - stringByAppendingPathComponent:@"/jre/lib/jli/libjli.dylib"]; - } + NSString *homeDir = [outRead stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + return findFirstFile(@"libjli.dylib", homeDir); + } @catch (NSException *exception) { @@ -564,3 +544,38 @@ int extractMajorVersion (NSString *vstring) NSString * convertRelativeFilePath(NSString * path) { return [path stringByStandardizingPath]; } + +NSString * findFirstFile(NSString * needle, NSString * haystack) { + + FTSENT *node = NULL; + + char *paths[] = {(char*) haystack.UTF8String, NULL}; + FTS* tree = fts_open(paths, FTS_NOCHDIR | FTS_LOGICAL | FTS_XDEV, NULL); + + NSString* foundFilePath = NULL; + + while ((node = fts_read(tree))) { + + // Skip folders that begin with a dot + if (node->fts_level > 0 && node->fts_name[0] == '.') { + fts_set(tree, node, FTS_SKIP); + + } else if (node->fts_info & FTS_F) { + + if (strcmp(node->fts_name, needle.UTF8String) == 0) { + foundFilePath = [NSString stringWithCString: node->fts_path encoding: NSUTF8StringEncoding]; + break; + } + } + } + + if (errno) { + NSLog (@"fts_read %d", errno); + } + + if (fts_close(tree)) { + NSLog (@"fts_close"); + } + + return foundFilePath; +}