changeset 39:89d7f4d91f67

Got a working, non-bloated Apple Mac bundle!
author David Barts <n5jrn@me.com>
date Fri, 01 May 2020 23:06:04 -0700
parents d794ef80f9b0
children a65a8f5a9647
files Building.html Makefile.mac build.xml package-files/osx/DS_Store package-files/osx/Info.plist package-files/osx/JavaApplicationStub package-files/osx/jedit
diffstat 7 files changed, 298 insertions(+), 20 deletions(-) [+]
line wrap: on
line diff
--- a/Building.html	Fri May 01 11:52:46 2020 -0700
+++ b/Building.html	Fri May 01 23:06:04 2020 -0700
@@ -29,6 +29,7 @@
       <li>Exiv2</li>
       <li>A C++ Compiler</li>
       <li>Make (Nmake on Windows)</li>
+      <li>Osdep</li>
     </ul>
     <h2>JpegWasher Is Not Pure Java</h2>
     <p>This means two things: </p>
--- a/Makefile.mac	Fri May 01 11:52:46 2020 -0700
+++ b/Makefile.mac	Fri May 01 23:06:04 2020 -0700
@@ -6,7 +6,10 @@
 
 .PHONY: all checkenv
 
-all: checkenv $(BDIR)/libjni.dylib $(BDIR)/libexiv2.dylib
+all: mkdirs checkenv $(BDIR)/libjni.dylib $(BDIR)/libexiv2.dylib
+
+mkdirs:
+	@[ -d "$(BDIR)" ] || mkdir -p "$(BDIR)"
 
 checkenv:
 	@if [ -z "$(JRE_HOME)" -o -z "$(EXIV2_HOME)" ]; then \
--- a/build.xml	Fri May 01 11:52:46 2020 -0700
+++ b/build.xml	Fri May 01 23:06:04 2020 -0700
@@ -1,7 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project name="JpegWasher" default="help" basedir="."
   xmlns:contrib="antlib:net.sf.antcontrib"
-  xmlns:jarbundler="antlib:com.ultramixer.jarbundler"
   xmlns:launch4j="antlib:net.sf.launch4j.ant">
   <!-- import all environment variables as env.* -->
   <property environment="env"/>
@@ -58,7 +57,8 @@
   <!-- Define the properties used by the build -->
   <property name="app.name"      value="${ant.project.name}"/>
   <property name="app.version"   value="1.03"/>
-  <property name="app.entry"     value="name.blackcap.exifwasher.MainKt"/>
+  <property name="app.domain"    value="name.blackcap.exifwasher"/>
+  <property name="app.entry"     value="${app.domain}.MainKt"/>
   <toLowerCase target="lc.app.name" value="${app.name}"/>
   <property name="jar.name"      value="${basedir}/${lc.app.name}.jar"/>
   <property name="work.jar"      value="${basedir}/work.jar"/>
@@ -68,8 +68,10 @@
   <property name="pf.home"       value="${basedir}/package-files"/>
   <property name="nat.dir"       value="${src.home}/name/blackcap/exifwasher/exiv2"/>
   <property name="bin.dir"       value="${src.home}/name/blackcap/exifwasher/binaries"/>
+  <property name="jvm.version"   value="1.8"/>
 
   <!-- load the ant-contrib tasks -->
+  <!-- TODO: see if we need this, remove if not (also see xmlns above) -->
   <taskdef resource="net/sf/antcontrib/antlib.xml"
            uri="antlib:net.sf.antcontrib">
     <classpath>
@@ -77,18 +79,6 @@
     </classpath>
   </taskdef>
 
-  <!-- load jarbundler (Mac app bundler) tasks -->
-  <contrib:if>
-    <os family="mac"/>
-    <contrib:then>
-      <taskdef name="create"
-               classname="com.ultramixer.jarbundler.JarBundler"
-               classpath="${lib.home}/jarbundler-core-3.3.0.jar"
-               uri="antlib:com.ultramixer.jarbundler">
-      </taskdef>
-    </contrib:then>
-  </contrib:if>
-
   <!-- load launch4j (Windows app bundler) tasks -->
   <contrib:if>
     <os family="windows"/>
@@ -151,7 +141,7 @@
 
   <!-- remove old cruft -->
   <target name="clean">
-    <delete includeEmptyDirs="true">
+    <delete includeEmptyDirs="true" failonerror="false">
       <fileset dir="${dist.home}" includes="**/*"/>
       <fileset dir="${bin.dir}"/>
     </delete>
@@ -159,7 +149,7 @@
 
   <!-- rename files containing OS-dependant code -->
   <target name="osdep">
-    <exec executable="${env.JRE_HOME}/bin/java">
+    <exec executable="${env.JRE_HOME}/bin/java" failonerror="true">
       <arg value="-jar"/>
       <arg value="../Osdep/osdep.jar"/>
       <arg value="${src.home}"/>
@@ -175,7 +165,7 @@
           description="Compile Java sources to ${work.home}">
     <kotlinc src="${src.home}" output="${work.jar}"
              classpathref="compile.classpath">
-      <compilerarg line="-jvm-target 1.8"/>
+      <compilerarg line="-jvm-target ${jvm.version}"/>
     </kotlinc>
   </target>
 
@@ -196,7 +186,73 @@
   <!-- the packaging logic here is cribbed from what jEdit does -->
 
   <target name="macapp" depends="jar" description="Create MacOS app bundle.">
-    <!-- not finished -->
+    <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.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>
 
   <target name="winapp" depends="jar" description="Create app bundle.">
@@ -204,6 +260,11 @@
   </target>
 
   <target name="deb" depends="jar" description="Create Debian package.">
+    <fail message="Linux packages can only be built on Linux.">
+      <condition>
+        <not><os family="unix"/></not>
+      </condition>
+    </fail>
     <sequential>
       <mkdir dir="${dist.home}/data/usr/share/applications"/>
       <copy file="${pf.home}/linux/deb/jpegwasher.desktop"
@@ -246,7 +307,7 @@
       <chmod perm="755">
         <filelist dir="${dist.home}/control" files="postinst,postrm"/>
       </chmod>
-      <exec executable="${basedir}/make-debian-package">
+      <exec executable="${basedir}/make-debian-package" failonerror="true">
         <arg value="${dist.home}"/>
         <arg value="${lc.app.name}_${app.version}.deb"/>
       </exec>
Binary file package-files/osx/DS_Store has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/package-files/osx/Info.plist	Fri May 01 23:06:04 2020 -0700
@@ -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>Copyright © 2020, David Barts</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	Fri May 01 23:06:04 2020 -0700
@@ -0,0 +1,166 @@
+#!/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()'
+#
+# read a specific Plist key with 'PlistBuddy' utility
+#
+# @param1  the Plist key with leading colon ':'
+# @return  the value as String or Array
+################################################################################
+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."
Binary file package-files/osx/jedit has changed