Last active
July 6, 2021 18:05
-
-
Save tresf/11038234bff464b301e62f21c96a7c77 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# Downloads, extracts and bundles the JDK into a runnable DMG for MacOS | |
# | |
# Dependencies: | |
# homebrew | |
# brew install node | |
# npm install -g appdmg | |
# xcode 12.0+ | |
# command line utilities | |
# keychain access | |
# a valid signing i.d. from apple | |
SIGNING_ID="P5DMU6659X" # tres@qz.io | |
# Calulate the download URL | |
if [ "$(uname -m)" = "arm64" ]; then | |
# aarch64 | |
JDK_URL="https://download.bell-sw.com/java/11.0.11+10/bellsoft-jdk11.0.11+10-macos-aarch64.zip" | |
else | |
# amd64 | |
JDK_URL="https://download.bell-sw.com/java/11.0.11+9/bellsoft-jdk11.0.11+9-macos-amd64.zip" | |
fi | |
# Download and extract | |
mkdir -p jdk | |
if [ "$1" = "-f" ]; then | |
rm -rf jdk/extracted | |
fi | |
echo | |
if [ -d jdk/extracted ]; then | |
echo "- JDK already downloaded (-f will force a re-download)" | |
else | |
echo "- Downloading the JDK from $JDK_URL..." | |
curl -o jdk/jdk.zip "$JDK_URL" | |
echo "- Unzipping the JDK to jdk/extracted..." | |
unzip jdk/jdk.zip -d jdk/extracted | |
fi | |
# Calculate JDK_HOME | |
# TODO: Some JDKs use different structure | |
pushd jdk/extracted > /dev/null | |
jdkfiles=(*) | |
JDK_RELATIVE="jdk/extracted/${jdkfiles[0]}" | |
popd > /dev/null | |
JDK_HOME="$(pwd)/$JDK_RELATIVE" | |
echo | |
echo "- Calculated JDK_HOME=$JDK_HOME" | |
echo " - Testing JDK..." | |
JDK_VERSION_RAW=$("$JDK_HOME/bin/java" --version 2>&1) | |
# TODO: Some JDKs return double quotes | |
JDK_VERSION="$(echo $JDK_VERSION_RAW | sed 's/ /\n/g' | sed -n 2p)" | |
JDK_VERSION_MAJOR=$(echo $JDK_VERSION | cut -d. -f1) | |
JDK_VERSION_MINOR=$(echo $JDK_VERSION | cut -d. -f2) | |
JDK_VERSION_PATCH=$(echo $JDK_VERSION | cut -d. -f3) | |
if [ -n "JDK_VERSION_PATCH" ]; then | |
echo " - Found version $JDK_VERSION_MAJOR.$JDK_VERSION_MINOR.$JDK_VERSION_PATCH" | |
else | |
echo " - BROKEN"; exit 1 | |
fi | |
# Create a sample Java application | |
echo | |
echo "- Creating a sample Java application..." | |
mkdir -p src | |
pushd src > /dev/null | |
cat >Hello.java <<EOL | |
import javax.print.*; | |
import javax.swing.*; | |
public class Hello { | |
public static void main(String ... args) throws Throwable { | |
SwingUtilities.invokeAndWait(() -> { | |
JOptionPane.showMessageDialog(null, "Hello world!" + | |
"\n JAVA_HOME: " + System.getProperty("java.home") + | |
"\n SANDBOXED: " + (System.getenv("APP_SANDBOX_CONTAINER_ID") != null) + | |
"\n PRINTER: " + PrintServiceLookup.lookupDefaultPrintService() + | |
"\n TOTAL: " + PrintServiceLookup.lookupPrintServices(null, null).length); | |
System.exit(0); | |
}); | |
} | |
} | |
EOL | |
if "$JDK_HOME/bin/javac" Hello.java ; then | |
echo " - Compiled successfully" | |
else | |
echo " - Compilation failed"; exit 1 | |
fi | |
echo "Main-Class: Hello" > MANIFEST.MF | |
rm -rf hello.jar | |
JAR_PATH="$(pwd)/hello.jar" | |
if "$JDK_HOME/bin/jar" cfm "$JAR_PATH" MANIFEST.MF Hello.class ; then | |
echo " - $JAR_PATH created" | |
else | |
echo " - $JAR_PATH creation failed"; exit 1 | |
fi | |
popd > /dev/null | |
# Setup some jlink tools | |
echo | |
echo "- Calculating some jlink tools..." | |
JMODS="$JDK_HOME/jmods" | |
JDEPS="$JDK_HOME/bin/jdeps" | |
JLINK="$JDK_HOME/bin/jlink" | |
# Make sure they work | |
echo " - JMODS=$JMODS" | |
if [ -d "$JMODS" ]; then | |
echo " - EXISTS" | |
else | |
echo " - MISSING"; exit 1 | |
fi | |
echo " - JDEPS=$JDEPS" | |
if "$JDEPS" --help >/dev/null; then | |
echo " - WORKS" | |
else | |
echo " - BROKEN"; exit 1 | |
fi | |
echo " - JLINK=$JLINK" | |
if "$JLINK" --help >/dev/null; then | |
echo " - WORKS" | |
else | |
echo " - BROKEN"; exit 1 | |
fi | |
# Start constructing a runtime from our jar | |
echo | |
echo "- Creating a suitable runtime in out/jlink for $JAR_PATH..." | |
# TODO: --ignore-missing-deps should be JDK 11.0.11+ only | |
echo " - Calculating dependencies..." | |
# TODO: For most apps, this will spew out unusable modules which need to be filtered | |
NEEDED_MODULES=$("$JDEPS" --list-deps --ignore-missing-deps "$JAR_PATH" |tr '\n' ',' |tr -d '[:space:]') | |
TARGET_APP=out/Hello.app | |
TARGET_RUNTIME="$TARGET_APP/Contents/PlugIns/Java.runtime" | |
echo " - Building $TARGET_RUNTIME..." | |
rm -rf "$TARGET_APP" | |
mkdir -p "$TARGET_RUNTIME/Contents" | |
"$JLINK" --strip-debug --compress=2 --no-header-files --no-man-pages --module-path "$JMODS" --add-modules $NEEDED_MODULES --output out/Hello.app/Contents/PlugIns/Java.runtime/Contents/Home | |
# Fix some layout nuances | |
echo | |
echo "- Fixing some Java.runtime layout nuances..." | |
echo " - Copying lib/jli/libjli.dylib to $TARGET_RUNTIME/Contents/MacOS..." | |
# TODO: Some JDKs offer this file in MacOS already, but jlink still won't deploy it. Copy from lib/jli is a guess? | |
mkdir -p "$TARGET_RUNTIME/Contents/MacOS" | |
if ! cp "$TARGET_RUNTIME/Contents/Home/lib/jli/libjli.dylib" "$TARGET_RUNTIME/Contents/MacOS" ; then | |
echo " - Failed"; exit 1 | |
fi | |
echo " - Removing unused files from $TARGET_RUNTIME/Contents/Home/bin..." | |
rm -f "$TARGET_RUNTIME/Contents/Home/bin/jfr" | |
rm -f "$TARGET_RUNTIME/Contents/Home/bin/rmid" | |
rm -f "$TARGET_RUNTIME/Contents/Home/bin/rmiregistry" | |
rm -f "$TARGET_RUNTIME/Contents/Home/bin/jrunscript" | |
rm -f "$TARGET_RUNTIME/Contents/Home/bin/keytool" | |
# Start building the app structure | |
echo | |
echo " - Building the main bundle..." | |
mkdir "$TARGET_APP/Contents/Resources" | |
echo " - Creating resources..." | |
cp /System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/Notifications.icns "$TARGET_APP/Contents/Resources/hello.icns" | |
cp "$JAR_PATH" "$TARGET_APP/Contents/Resources" | |
echo " - Creating launcher..." | |
mkdir "$TARGET_APP/Contents/MacOS" | |
cat >"$TARGET_APP/Contents/MacOS/Hello" <<EOL | |
#!/bin/bash | |
SCRIPT_DIR="\$( cd "\$( dirname "\${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" | |
pushd "\$SCRIPT_DIR/../PlugIns/Java.runtime/Contents/home/bin" | |
./java -jar "\$SCRIPT_DIR/../Resources/hello.jar" | |
popd | |
EOL | |
chmod +x "$TARGET_APP/Contents/MacOS/Hello" | |
echo " - Writing Info.plist..." | |
cat>"$TARGET_APP/Contents/Info.plist" <<EOL | |
<?xml version="1.0" encoding="UTF-8"?> | |
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |
<plist version="1.0"><dict> | |
<key>CFBundleDevelopmentRegion</key><string>English</string> | |
<key>CFBundleIconFile</key><string>hello</string> | |
<key>CFBundleIdentifier</key><string>io.qz.hello</string> | |
<key>CFBundlePackageType</key><string>APPL</string> | |
<key>CFBundleGetInfoString</key><string>Hello 1.0.0</string> | |
<key>CFBundleSignature</key><string>Hello</string> | |
<key>CFBundleExecutable</key><string>Hello</string> | |
<key>CFBundleVersion</key><string>1.0.0</string> | |
<key>CFBundleShortVersionString</key><string>1.0.0</string> | |
<key>CFBundleName</key><string>Hello</string> | |
<key>CFBundleInfoDictionaryVersion</key><string>6.0</string> | |
</dict></plist> | |
EOL | |
echo " - You should have a usable app now, but we're going to try to sign and hopefully sandbox it." | |
echo | |
echo "- Preparing the entitlement files..." | |
echo " - Writing bundle entitlements to src/entitlements.plist..." | |
cat>"src/entitlements.plist" <<EOL | |
<?xml version="1.0" encoding="UTF-8"?> | |
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |
<plist version="1.0"> | |
<dict> | |
<key>com.apple.security.app-sandbox</key><true/> | |
<key>com.apple.security.network.client</key><true/> | |
<key>com.apple.security.network.server</key><true/> | |
<key>com.apple.security.files.all</key><true/> | |
<key>com.apple.security.print</key><true/> | |
<key>com.apple.security.device.usb</key><true/> | |
<key>com.apple.security.device.bluetooth</key><true/> | |
<key>com.apple.security.cs.allow-jit</key><true/> | |
<key>com.apple.security.cs.allow-unsigned-executable-memory</key><true/> | |
<key>com.apple.security.cs.disable-library-validation</key><true/> | |
<key>com.apple.security.cs.allow-dyld-environment-variables</key><true/> | |
<key>com.apple.security.cs.debugger</key><true/> | |
</dict> | |
</plist> | |
EOL | |
echo " - Writing Info.plist for $TARGET_RUNTIME..." | |
# TODO: Write vendor and version information dynamically | |
cat>"$TARGET_RUNTIME/Contents/Info.plist" <<EOL | |
<?xml version="1.0" encoding="UTF-8"?> | |
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |
<plist version="1.0"> | |
<dict> | |
<key>CFBundleDevelopmentRegion</key><string>English</string> | |
<key>CFBundleExecutable</key><string>libjli.dylib</string> | |
<key>CFBundleGetInfoString</key><string>BellSoft 11.0.11+10</string> | |
<key>CFBundleIdentifier</key><string>io.qz.hello.jre</string> | |
<key>CFBundleInfoDictionaryVersion</key><string>7.0</string> | |
<key>CFBundleName</key><string>Java Runtime Image</string> | |
<key>CFBundlePackageType</key><string>BNDL</string> | |
<key>CFBundleShortVersionString</key><string>11.0.11</string> | |
<key>CFBundleSignature</key><string>????</string> | |
<key>CFBundleVersion</key><string>11.0.11</string> | |
<key>JavaVM</key> | |
<dict> | |
<key>JVMCapabilities</key> | |
<array> | |
<string>CommandLine</string> | |
<string>JNI</string> | |
<string>BundledApp</string> | |
</array> | |
<key>JVMMinimumFrameworkVersion</key><string>17.0.0</string> | |
<key>JVMMinimumSystemVersion</key><string>10.6.0</string> | |
<key>JVMPlatformVersion</key><string>11.0.11+10</string> | |
<key>JVMVendor</key><string>BellSoft</string> | |
<key>JVMVersion</key><string>11.0.11</string> | |
</dict> | |
</dict> | |
</plist> | |
EOL | |
echo "- Starting the signing process..." | |
echo " - Signing individual files to overwrite any lingering signatures..." | |
find -X "$TARGET_APP" -type f -not -path "*/Contents/MacOS/*" -exec sh -c 'file -I "{}" |grep -m1 "x-mach-binary"|cut -f 1 -d \:' \; |xargs codesign --force -s "$SIGNING_ID" --timestamp --options runtime --entitlement src/entitlements.plist | |
if [ $? -ne 0 ]; then | |
echo " - Failed."; exit 1 | |
fi | |
echo " - Signing the Java.runtime bundle using src/entitlements.plist..." | |
if ! codesign --force -s $SIGNING_ID --timestamp --options runtime --entitlement src/entitlements.plist "$TARGET_RUNTIME" ; then | |
echo " - Failed."; exit 1 | |
fi | |
echo " - Signing the main bundle using src/entitlements.plist..." | |
if ! codesign --force -s $SIGNING_ID --timestamp --options runtime --entitlement src/entitlements.plist "$TARGET_APP" ; then | |
echo " - Failed."; exit 1 | |
fi | |
echo " - The signed app is available here: $TARGET_APP" | |
echo "- Packaging the DMG..." | |
# TODO: ../$TARGET_APP needed because appdmg works relative to json file | |
cat>"src/dmg.json" <<EOL | |
{ | |
"title": "Hello", | |
"contents": [ | |
{ "x": 407, "y": 244, "type": "link", "path": "/Applications" }, | |
{ "x": 133, "y": 244, "type": "file", "path": "../$TARGET_APP" } | |
] | |
} | |
EOL | |
rm -f "out/Hello 1.0.0.dmg" | |
if ! appdmg "src/dmg.json" "out/Hello 1.0.0.dmg" ; then | |
echo " - Failed."; exit 1 | |
fi | |
echo " - The DMG is available at out/Hello 1.0.0.dmg" | |
echo | |
echo "- Finished" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment