changeset 23:92afaa27f40a

Mac app support.
author David Barts <n5jrn@me.com>
date Tue, 24 Nov 2020 16:45:37 -0800
parents d3979a2155a8
children a4737a525af8
files ImagePrep.icns ImagePrep.iconset/icon_128x128.png ImagePrep.iconset/icon_128x128@2x.png ImagePrep.iconset/icon_16x16.png ImagePrep.iconset/icon_16x16@2x.png ImagePrep.iconset/icon_256x256.png ImagePrep.iconset/icon_256x256@2x.png ImagePrep.iconset/icon_32x32.png ImagePrep.iconset/icon_32x32@2x.png ImagePrep.iconset/icon_512x512.png ImagePrep.iconset/icon_512x512@2x.png build.xml package-files/osx/Info.plist package-files/osx/JavaApplicationStub src/name/blackcap/imageprep/Main.kt src/name/blackcap/imageprep/Menus.kt
diffstat 16 files changed, 279 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
Binary file ImagePrep.icns has changed
Binary file ImagePrep.iconset/icon_128x128.png has changed
Binary file ImagePrep.iconset/icon_128x128@2x.png has changed
Binary file ImagePrep.iconset/icon_16x16.png has changed
Binary file ImagePrep.iconset/icon_16x16@2x.png has changed
Binary file ImagePrep.iconset/icon_256x256.png has changed
Binary file ImagePrep.iconset/icon_256x256@2x.png has changed
Binary file ImagePrep.iconset/icon_32x32.png has changed
Binary file ImagePrep.iconset/icon_32x32@2x.png has changed
Binary file ImagePrep.iconset/icon_512x512.png has changed
Binary file ImagePrep.iconset/icon_512x512@2x.png has changed
--- a/build.xml	Mon Nov 23 15:45:04 2020 -0800
+++ b/build.xml	Tue Nov 24 16:45:37 2020 -0800
@@ -41,6 +41,7 @@
   <property name="lib.home"      value="${basedir}/lib"/>
   <property name="src.home"      value="${basedir}/src"/>
   <property name="dist.home"     value="${basedir}/dist"/>
+  <property name="pf.home"       value="${basedir}/package-files"/>
   <property name="jvm.version"   value="1.8"/>
 
   <!-- define the kotlin task -->
@@ -119,4 +120,75 @@
     </jar>
   </target>
 
+  <target name="macapp" depends="jar" description="Create MacOS app bundle.">
+    <fail message="Macintosh packages can only be built on a Mac.">
+      <condition>
+        <not><os family="mac"/></not>
+      </condition>
+    </fail>
+    <sequential>
+      <property name="mac.disk.image.filename"
+                value="${lc.app.name}_${app.version}.dmg"/>
+      <property name="app.bundle" value="${dist.home}/${app.name}.app"/>
+      <mkdir dir="${app.bundle}/Contents"/>
+      <copy todir="${app.bundle}/Contents" encoding="UTF-8" overwrite="true">
+        <fileset file="${pf.home}/osx/Info.plist"/>
+        <!-- XXX will break if any tokens contain <, >, or & -->
+        <filterset>
+          <filter token="app.copyright" value="${app.copyright}"/>
+          <filter token="app.domain" value="${app.domain}"/>
+          <filter token="app.entry" value="${app.entry}"/>
+          <filter token="app.name" value="${app.name}"/>
+          <filter token="app.version" value="${app.version}"/>
+          <filter token="jar.filename" value="${lc.app.name}.jar"/>
+          <filter token="jvm.version" value="${jvm.version}"/>
+          <filter token="lc.app.name" value="${lc.app.name}"/>
+        </filterset>
+      </copy>
+      <mkdir dir="${app.bundle}/Contents/MacOS"/>
+      <copy todir="${app.bundle}/Contents/MacOS" encoding="UTF-8"
+            overwrite="true">
+        <fileset file="${pf.home}/osx/JavaApplicationStub"/>
+        <filterset>
+          <filter token="app.domain" value="${app.domain}"/>
+          <filter token="app.name" value="${app.name}"/>
+        </filterset>
+      </copy>
+      <chmod file="${app.bundle}/Contents/MacOS/JavaApplicationStub"
+             perm="755"/>
+      <mkdir dir="${app.bundle}/Contents/Resources"/>
+      <copy file="${basedir}/${app.name}.icns"
+            todir="${app.bundle}/Contents/Resources"/>
+      <mkdir dir="${app.bundle}/Contents/Java"/>
+      <copy file="${jar.name}" todir="${app.bundle}/Contents/Java"/>
+      <echo file="${app.bundle}/Contents/PkgInfo" message="APPL????"/>
+      <exec executable="hdiutil" failonerror="true">
+        <arg value="create"/>
+        <arg value="-volname"/>
+        <arg value="${app.name}"/>
+        <arg value="-srcfolder"/>
+        <arg file="${app.bundle}"/>
+        <arg file="${dist.home}/orig-${mac.disk.image.filename}"/>
+      </exec>
+      <exec executable="hdiutil" failonerror="true">
+        <arg value="convert"/>
+        <arg file="${dist.home}/orig-${mac.disk.image.filename}"/>
+        <arg value="-format"/>
+        <arg value="UDRW"/>
+        <arg value="-o"/>
+        <arg file="${dist.home}/udrw-${mac.disk.image.filename}"/>
+      </exec>
+      <exec executable="hdiutil" failonerror="true">
+        <arg value="convert"/>
+        <arg file="${dist.home}/udrw-${mac.disk.image.filename}"/>
+        <arg value="-format"/>
+        <arg value="UDZO"/>
+        <arg value="-imagekey"/>
+        <arg value="zlib-level=9"/>
+        <arg value="-o"/>
+        <arg file="${dist.home}/${mac.disk.image.filename}"/>
+      </exec>
+    </sequential>
+  </target>
+
 </project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/package-files/osx/Info.plist	Tue Nov 24 16:45:37 2020 -0800
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<plist version="1.0">
+  <!-- see: https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/AboutInformationPropertyListFiles.html -->
+  <dict>
+    <!-- Apple says the following are a must -->
+    <key>CFBundleDevelopmentRegion</key>
+    <string>English</string>
+    <key>CFBundleDisplayName</key>
+    <string>@app.name@</string>
+    <key>CFBundleExecutable</key>
+    <string>JavaApplicationStub</string>
+    <key>CFBundleIconFile</key>
+    <string>@app.name@.icns</string>
+    <key>CFBundleIdentifier</key>
+    <string>@app.domain@</string>
+    <key>CFBundleInfoDictionaryVersion</key>
+    <string>6.0</string>
+    <key>CFBundleName</key>
+    <string>@app.name@</string>
+    <key>CFBundlePackageType</key>
+    <string>APPL</string>
+    <key>CFBundleShortVersionString</key>
+    <string>@app.version@</string>
+    <key>CFBundleVersion</key>
+    <string>@app.version@</string>
+    <key>NSHumanReadableCopyright</key>
+    <string>@app.copyright@</string>
+    <!-- most package builders throw the following in -->
+    <key>CFBundleAllowMixedLocalizations</key>
+    <string>false</string>
+    <key>LSApplicationCategoryType</key>
+    <string>public.app-category.photography</string>
+    <!-- JavaApplicationStub wants the following stuff -->
+    <key>JVMMainClassName</key>
+    <string>@app.entry@</string>
+    <key>JVMClassPath</key>
+    <string>Contents/Java/@lc.app.name@.jar</string>
+    <key>JVMVersion</key>
+    <string>@jvm.version@+</string>
+    <key>JVMOptions</key>
+    <array>
+      <string>-Dapple.awt.textantialiasing=true</string>
+      <string>-Dapple.laf.useScreenMenuBar=true</string>
+      <string>-Dapple.awt.antialiasing=true</string>
+    </array>
+  </dict>
+</plist>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/package-files/osx/JavaApplicationStub	Tue Nov 24 16:45:37 2020 -0800
@@ -0,0 +1,159 @@
+#!/bin/bash
+
+# This is based on Tobias Fischer's universalJavaApplicationStub:
+# https://github.com/tofi86/universalJavaApplicationStub
+#
+# Therefore this part of JpegWasher is covered by the same MIT license
+# that his code is:
+#
+# The MIT License (MIT)
+#
+# Copyright (c) 2014-2020 Tobias Fischer
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+# Establish our application names
+appname="@app.name@"
+appdom="@app.domain@"
+
+# Make a place for our logs to go
+appsupp="$HOME/Library/Application Support/$appdom"
+logfile="$appsupp/launcher.log"
+
+function die {
+    osascript -e "tell application \"System Events\" to display dialog \"${1}\" with title \"Fatal Error\" buttons {\" OK \"} default button 1"
+    exit ${2:-1}
+}
+
+# Initialize files and dirs, bail if we can't.
+if [ ! -d "$appsupp" ]
+then
+    if ! mkdir -p "$appsupp"
+    then
+		die "Unable to create folder '$appsupp'" 2
+    fi
+fi
+> "$logfile"
+if [ $? -ne 0 ]
+then
+    die "Unable to create file '$logfile'" 2
+fi
+
+function stub_logger {
+    echo "$(date +%c) - $1" >> "$logfile"
+}
+
+# Was originally just for debugging, but I think this could be useful
+# for later troubleshooting "in the wild."
+stub_logger "execution begins"
+{
+    echo "****** BEGIN ARGUMENT AND ENVIRONMENT DUMP *******"
+    echo "Arguments:" "$0" "$@"
+    echo "Directory:" $(pwd)
+    env
+    echo "******* END ARGUMENT AND ENVIRONMENT DUMP ********"
+} >> "$logfile"
+
+# Change to the application root directory, two up from where this script
+# (which should contain an absolute path) is.
+mydir="${0%/*}"
+cd "$mydir/../.." || die "Unable to cd '$mydir/../..'"
+
+# Determine where Java is, bail if we can't.
+if [ -n "$JAVA_HOME" ]
+then
+    if [ ! -d "$JAVA_HOME" ]
+    then
+        die "'$JAVA_HOME' does not exist"
+    fi
+else
+    export JAVA_HOME="$(/usr/libexec/java_home)"
+    if [ $? -ne 0 -o ! -d "$JAVA_HOME" ]
+    then
+        die "Unable to locate Java."
+    fi
+fi
+
+java="$JAVA_HOME/bin/java"
+if [ ! -x "$java" ]
+then
+    die "'$java' not found or not executable"
+fi
+
+function plist_get {
+	/usr/libexec/PlistBuddy -c "print $1" Contents/Info.plist 2> /dev/null
+}
+function must_get {
+    plist_get "$@" || die "Info.plist key '$1' not found."
+}
+
+# Load some parameters from Contents/Info.plist
+estat=0
+CFBundleName=$(must_get ':CFBundleName')
+CFBundleIconFile=$(must_get ':CFBundleIconFile')
+JVMMainClassName=$(must_get ':JVMMainClassName')
+JVMClassPath=$(must_get ':JVMClassPath')
+JVMVersion=$(must_get ':JVMVersion')
+
+# Sanity check our JVM version
+if [[ "$JVMVersion" == *+ ]]
+then
+    op='>'
+    JVMVersion="${JVMVersion%+}"
+else
+    op='='
+fi
+version=$("$java" -version 2>&1 | awk '/version/{print $3}' | sed -E -e 's/"//g' -e 's/-ea//g')
+if [ ! "$version" "$op" "$JVMVersion" ]
+then
+    die "'$java' too old"
+fi
+
+# Load the JVMOptions array (if found)
+JVMOptions=()
+let i=0
+while true
+do
+    j="$(plist_get :JVMOptions:$i)"
+    if [ -z "$j" ]
+    then
+        break
+    fi
+    JVMOptions+=("$j")
+    let i+=1
+done
+
+# Fatal error if we cannot change to HOME or HOME not defined
+approot="$(pwd)"
+if [ -z "$HOME" ]
+then
+    die "HOME not defined!"
+fi
+cd "$HOME" || die "Unable to cd '$HOME'"
+
+# And off we go!
+jcmd=( "$JAVA_HOME/bin/java" -cp "$approot/$JVMClassPath"
+    -Xdock:icon="$approot/Contents/Resources/$CFBundleIconFile"
+    -Xdock:name="$CFBundleName" "${JVMOptions[@]}" "$JVMMainClassName" )
+
+stub_logger "Executing: ${jcmd[*]}"
+exec "${jcmd[@]}"
+
+# This shouldn't happen...
+die "Could not launch Java."
--- a/src/name/blackcap/imageprep/Main.kt	Mon Nov 23 15:45:04 2020 -0800
+++ b/src/name/blackcap/imageprep/Main.kt	Tue Nov 24 16:45:37 2020 -0800
@@ -7,13 +7,6 @@
 import java.util.logging.Level
 import java.util.logging.Logger
 import javax.swing.UIManager
-import name.blackcap.kcli.CommandLine
-import name.blackcap.kcli.InvalidArgumentException
-import name.blackcap.kcli.Option
-import name.blackcap.kcli.PromptingParser
-import org.apache.commons.cli.HelpFormatter
-import org.apache.commons.cli.Options
-import org.apache.commons.cli.ParseException
 
 object Application {
     /* name we call ourselves */
--- a/src/name/blackcap/imageprep/Menus.kt	Mon Nov 23 15:45:04 2020 -0800
+++ b/src/name/blackcap/imageprep/Menus.kt	Tue Nov 24 16:45:37 2020 -0800
@@ -89,7 +89,7 @@
         val acc = JPanel().apply {
             layout = BoxLayout(this, BoxLayout.Y_AXIS)
             add(Box.createGlue())
-            add(JLabel("Max. dimension:").apply { alignmentX = LEFT_ALIGNMENT })
+            add(JLabel("Max. dim.:").apply { alignmentX = LEFT_ALIGNMENT })
             add(maxDim)
         }
         val chooser = JFileChooser().apply {