From aec627646ef28c867c199cf2956b74f2ae38e7b2 Mon Sep 17 00:00:00 2001 From: Yee Cheng Chin Date: Thu, 13 Aug 2020 02:36:25 -0700 Subject: [PATCH] Update create-dmg to work in macOS Catalina 10.15 --- src/MacVim/dmg/README.md | 1 + src/{create-dmg => MacVim/dmg}/background.png | Bin src/{create-dmg => MacVim/dmg}/background.pxd | Bin src/Makefile | 2 +- src/create-dmg/.this-is-the-create-dmg-repo | 0 src/create-dmg/LICENSE | 1 + src/create-dmg/README | 3 +- src/create-dmg/README.md | 102 ++++ src/create-dmg/create-dmg | 544 ++++++++++++------ src/create-dmg/doc-project/Developer Notes.md | 35 ++ .../doc-project/Release Checklist.md | 10 + .../01-main-example/installer_background.png | Bin 0 -> 35656 bytes .../examples/01-main-example/sample | 25 + .../source_folder/Application.app | 0 .../support/LICENSE-licenseDMG-pyhacker | 19 + src/create-dmg/support/README | 7 + src/create-dmg/support/dmg-license.py | 163 ------ src/create-dmg/support/licenseDMG.py | 200 +++++++ src/create-dmg/support/template.applescript | 37 +- .../007-space-in-dir-name/my files/hello.txt | 1 + .../tests/007-space-in-dir-name/run-test | 5 + 21 files changed, 800 insertions(+), 355 deletions(-) create mode 100644 src/MacVim/dmg/README.md rename src/{create-dmg => MacVim/dmg}/background.png (100%) rename src/{create-dmg => MacVim/dmg}/background.pxd (100%) create mode 100644 src/create-dmg/.this-is-the-create-dmg-repo create mode 100644 src/create-dmg/README.md create mode 100644 src/create-dmg/doc-project/Developer Notes.md create mode 100644 src/create-dmg/doc-project/Release Checklist.md create mode 100644 src/create-dmg/examples/01-main-example/installer_background.png create mode 100755 src/create-dmg/examples/01-main-example/sample create mode 100644 src/create-dmg/examples/01-main-example/source_folder/Application.app create mode 100644 src/create-dmg/support/LICENSE-licenseDMG-pyhacker create mode 100644 src/create-dmg/support/README delete mode 100755 src/create-dmg/support/dmg-license.py create mode 100755 src/create-dmg/support/licenseDMG.py create mode 100644 src/create-dmg/tests/007-space-in-dir-name/my files/hello.txt create mode 100755 src/create-dmg/tests/007-space-in-dir-name/run-test diff --git a/src/MacVim/dmg/README.md b/src/MacVim/dmg/README.md new file mode 100644 index 0000000000..9245c013c6 --- /dev/null +++ b/src/MacVim/dmg/README.md @@ -0,0 +1 @@ +This folder contains resources used for creating the dmg installation file. diff --git a/src/create-dmg/background.png b/src/MacVim/dmg/background.png similarity index 100% rename from src/create-dmg/background.png rename to src/MacVim/dmg/background.png diff --git a/src/create-dmg/background.pxd b/src/MacVim/dmg/background.pxd similarity index 100% rename from src/create-dmg/background.pxd rename to src/MacVim/dmg/background.pxd diff --git a/src/Makefile b/src/Makefile index f9e5431979..71e04332a2 100644 --- a/src/Makefile +++ b/src/Makefile @@ -3752,7 +3752,7 @@ macvim-dmg: create-dmg/create-dmg \ --volname "MacVim" \ --volicon MacVim/icons/MacVim.icns \ - --background create-dmg/background.png \ + --background MacVim/dmg/background.png \ --window-size 650 470 \ --icon-size 80 \ --icon MacVim.app 240 320 \ diff --git a/src/create-dmg/.this-is-the-create-dmg-repo b/src/create-dmg/.this-is-the-create-dmg-repo new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/create-dmg/LICENSE b/src/create-dmg/LICENSE index 349b6d82b3..deb407f2ee 100644 --- a/src/create-dmg/LICENSE +++ b/src/create-dmg/LICENSE @@ -1,6 +1,7 @@ The MIT License (MIT) Copyright (c) 2008-2014 Andrey Tarantsov +Copyright (c) 2020 Andrew Janke Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/create-dmg/README b/src/create-dmg/README index 4557795edb..170da8de1e 100644 --- a/src/create-dmg/README +++ b/src/create-dmg/README @@ -1,2 +1 @@ -Content of this directory (except background.png) is copied from -https://github.com/andreyvit/create-dmg (5acf22f). +Content of this directory is copied from https://github.com/create-dmg/create-dmg (v1.0.8 / 0985398). diff --git a/src/create-dmg/README.md b/src/create-dmg/README.md new file mode 100644 index 0000000000..f71dae337d --- /dev/null +++ b/src/create-dmg/README.md @@ -0,0 +1,102 @@ +create-dmg +========== + +A shell script to build fancy DMGs. + +Status and contribution policy +------------------------------ + +Create-dmg is maintained thanks to the contributors who send pull requests. +As of May 2020, [Andrew Janke](https://github.com/apjanke) is the primary maintainer, and (since September 2018) [@aonez](https://github.com/aonez) has helped with the maintenance. +The project home page is . + +We will merge any pull request that adds something useful and does not break existing things. + +If you're an active user and want to be a maintainer, or just want to chat, please ping us on Gitter at [gitter.im/create-dmg/Lobby](https://gitter.im/create-dmg/Lobby), or [email Andrew directly](floss@apjanke.net). + +Create-dmg was originally created by [Andrey Tarantsov](https://github.com/andreyvit). + +Installation +------------ + +- You can install this script using [Homebrew](https://brew.sh): + + ```sh + brew install create-dmg + ``` + +- You can download the [latest release](https://github.com/create-dmg/create-dmg/releases/latest) and install it from there: + + ```sh + make install + ``` + +- You can also clone the entire repository and run it locally from there: + + ```sh + git clone https://github.com/create-dmg/create-dmg.git + ``` + +Usage +----- + +```sh +create-dmg [options ...] +``` + +All contents of source\_folder will be copied into the disk image. + +**Options:** + +- **--volname \:** set volume name (displayed in the Finder sidebar and window title) +- **--volicon \:** set volume icon +- **--background \:** set folder background image (provide png, gif, jpg) +- **--window-pos \ \:** set position the folder window +- **--window-size \ \:** set size of the folder window +- **--text-size \:** set window text size (10-16) +- **--icon-size \:** set window icons size (up to 128) +- **--icon \ \ \:** set position of the file's icon +- **--hide-extension \:** hide the extension of file +- **--custom-icon \ \ \:** set position and -tom icon +- **--app-drop-link \ \:** make a drop link to Applications, at location x, y +- **--ql-drop-link \ \:** make a drop link to /Library/QuickLook, at location x, y +- **--eula \:** attach a license file to the dmg +- **--rez \:** specify custom path to Rez tool used to include license file +- **--no-internet-enable:** disable automatic mount© +- **--format:** specify the final image format (default is UDZO) +- **--add-file \ \ \ \:** add additional file or folder (can be used multiple times) +- **--disk-image-size \:** set the disk image size manually to x MB +- **--hdiutil-verbose:** execute hdiutil in verbose mode +- **--hdiutil-quiet:** execute hdiutil in quiet mode +- **--sandbox-safe:** execute hdiutil with sandbox compatibility and do not bless +- **--version:** show tool version number +- **-h, --help:** display the help + +Example +------- + +```sh +#!/bin/sh +test -f Application-Installer.dmg && rm Application-Installer.dmg +create-dmg \ + --volname "Application Installer" \ + --volicon "application_icon.icns" \ + --background "installer_background.png" \ + --window-pos 200 120 \ + --window-size 800 400 \ + --icon-size 100 \ + --icon "Application.app" 200 190 \ + --hide-extension "Application.app" \ + --app-drop-link 600 185 \ + "Application-Installer.dmg" \ + "source_folder/" +``` + +See the `examples` folder in the source tree for more examples. + +Alternatives +------------ + +- [node-appdmg](https://github.com/LinusU/node-appdmg) +- [dmgbuild](https://pypi.python.org/pypi/dmgbuild) +- see the [StackOverflow question](http://stackoverflow.com/questions/96882/how-do-i-create-a-nice-looking-dmg-for-mac-os-x-using-command-line-tools) diff --git a/src/create-dmg/create-dmg b/src/create-dmg/create-dmg index 3e55dd9742..79185a5a21 100755 --- a/src/create-dmg/create-dmg +++ b/src/create-dmg/create-dmg @@ -1,53 +1,15 @@ -#! /bin/bash +#!/usr/bin/env bash # Create a read-only disk image of the contents of a folder +# Bail out on any unhandled errors set -e; -function pure_version() { - echo '1.0.0.2' -} +CDMG_VERSION='1.0.8' -function version() { - echo "create-dmg $(pure_version)" -} - -function usage() { - version - echo "Creates a fancy DMG file." - echo "Usage: $(basename $0) options... image.dmg source_folder" - echo "All contents of source_folder will be copied into the disk image." - echo "Options:" - echo " --volname name" - echo " set volume name (displayed in the Finder sidebar and window title)" - echo " --volicon icon.icns" - echo " set volume icon" - echo " --background pic.png" - echo " set folder background image (provide png, gif, jpg)" - echo " --window-pos x y" - echo " set position the folder window" - echo " --window-size width height" - echo " set size of the folder window" - echo " --text-size text_size" - echo " set window text size (10-16)" - echo " --icon-size icon_size" - echo " set window icons size (up to 128)" - echo " --icon file_name x y" - echo " set position of the file's icon" - echo " --hide-extension file_name" - echo " hide the extension of file" - echo " --custom-icon file_name custom_icon_or_sample_file x y" - echo " set position and custom icon" - echo " --app-drop-link x y" - echo " make a drop link to Applications, at location x,y" - echo " --eula eula_file" - echo " attach a license file to the dmg" - echo " --no-internet-enable" - echo " disable automatic mount©" - echo " --version show tool version number" - echo " -h, --help display this help" - exit 0 -} +# The full path to the "support/" directory this script is using +# (This will be set up by code later in the script.) +CDMG_SUPPORT_DIR="" WINX=10 WINY=60 @@ -55,176 +17,422 @@ WINW=500 WINH=350 ICON_SIZE=128 TEXT_SIZE=16 +FORMAT="UDZO" +ADD_FILE_SOURCES=() +ADD_FILE_TARGETS=() +IMAGEKEY="" +HDIUTIL_VERBOSITY="" +SANDBOX_SAFE=0 +SKIP_JENKINS=0 -while test "${1:0:1}" = "-"; do - case $1 in - --volname) - VOLUME_NAME="$2" - shift; shift;; - --volicon) - VOLUME_ICON_FILE="$2" - shift; shift;; - --background) - BACKGROUND_FILE="$2" - BACKGROUND_FILE_NAME="$(basename $BACKGROUND_FILE)" - BACKGROUND_CLAUSE="set background picture of opts to file \".background:$BACKGROUND_FILE_NAME\"" - REPOSITION_HIDDEN_FILES_CLAUSE="set position of every item to {theBottomRightX + 100, 100}" - shift; shift;; - --icon-size) - ICON_SIZE="$2" - shift; shift;; - --text-size) - TEXT_SIZE="$2" - shift; shift;; - --window-pos) - WINX=$2; WINY=$3 - shift; shift; shift;; - --window-size) - WINW=$2; WINH=$3 - shift; shift; shift;; - --icon) - POSITION_CLAUSE="${POSITION_CLAUSE}set position of item \"$2\" to {$3, $4} -" - shift; shift; shift; shift;; - --hide-extension) - HIDING_CLAUSE="${HIDING_CLAUSE}set the extension hidden of item \"$2\" to true -" - shift; shift;; - --custom-icon) - shift; shift; shift; shift; shift;; - -h | --help) - usage;; - --version) - version; exit 0;; - --pure-version) - pure_version; exit 0;; - --app-drop-link) - APPLICATION_LINK=$2 - APPLICATION_CLAUSE="set position of item \"Applications\" to {$2, $3} -" - shift; shift; shift;; - --eula) - EULA_RSRC=$2 - shift; shift;; - --no-internet-enable) - NOINTERNET=1 - shift;; - -*) - echo "Unknown option $1. Run with --help for help." - exit 1;; - esac -done - -test -z "$2" && { - echo "Not enough arguments. Invoke with --help for help." - exit 1 +function pure_version() { + echo "$CDMG_VERSION" } -SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +function version() { + echo "create-dmg $(pure_version)" +} + +function usage() { + version + cat < + +All contents of will be copied into the disk image. + +Options: + --volname + set volume name (displayed in the Finder sidebar and window title) + --volicon + set volume icon + --background + set folder background image (provide png, gif, or jpg) + --window-pos + set position the folder window + --window-size + set size of the folder window + --text-size + set window text size (10-16) + --icon-size + set window icons size (up to 128) + --icon file_name + set position of the file's icon + --hide-extension + hide the extension of file + --custom-icon + set position and custom icon + --app-drop-link + make a drop link to Applications, at location x,y + --ql-drop-link + make a drop link to user QuickLook install dir, at location x,y + --eula + attach a license file to the dmg (plain text or RTF) + --no-internet-enable + disable automatic mount & copy + --format + specify the final image format (default is UDZO) + --add-file | + add additional file or folder (can be used multiple times) + --disk-image-size + set the disk image size manually to x MB + --hdiutil-verbose + execute hdiutil in verbose mode + --hdiutil-quiet + execute hdiutil in quiet mode + --sandbox-safe + execute hdiutil with sandbox compatibility and do not bless + --rez + use custom path to Rez tool + --version + show create-dmg version number + -h, --help + display this help screen + +EOHELP + exit 0 +} + +# Argument parsing + +while [[ "${1:0:1}" = "-" ]]; do + case $1 in + --volname) + VOLUME_NAME="$2" + shift; shift;; + --volicon) + VOLUME_ICON_FILE="$2" + shift; shift;; + --background) + BACKGROUND_FILE="$2" + BACKGROUND_FILE_NAME="$(basename "$BACKGROUND_FILE")" + BACKGROUND_CLAUSE="set background picture of opts to file \".background:$BACKGROUND_FILE_NAME\"" + REPOSITION_HIDDEN_FILES_CLAUSE="set position of every item to {theBottomRightX + 100, 100}" + shift; shift;; + --icon-size) + ICON_SIZE="$2" + shift; shift;; + --text-size) + TEXT_SIZE="$2" + shift; shift;; + --window-pos) + WINX=$2; WINY=$3 + shift; shift; shift;; + --window-size) + WINW=$2; WINH=$3 + shift; shift; shift;; + --icon) + POSITION_CLAUSE="${POSITION_CLAUSE}set position of item \"$2\" to {$3, $4} + " + shift; shift; shift; shift;; + --hide-extension) + HIDING_CLAUSE="${HIDING_CLAUSE}set the extension hidden of item \"$2\" to true + " + shift; shift;; + --custom-icon) + shift; shift; shift; shift; shift;; + -h | --help) + usage;; + --version) + version; exit 0;; + --pure-version) + pure_version; exit 0;; + --ql-drop-link) + QL_LINK=$2 + QL_CLAUSE="set position of item \"QuickLook\" to {$2, $3} + " + shift; shift; shift;; + --app-drop-link) + APPLICATION_LINK=$2 + APPLICATION_CLAUSE="set position of item \"Applications\" to {$2, $3} + " + shift; shift; shift;; + --eula) + EULA_RSRC=$2 + shift; shift;; + --no-internet-enable) + NOINTERNET=1 + shift;; + --format) + FORMAT="$2" + shift; shift;; + --add-file | --add-folder) + ADD_FILE_TARGETS+=("$2") + ADD_FILE_SOURCES+=("$3") + POSITION_CLAUSE="${POSITION_CLAUSE} + set position of item \"$2\" to {$4, $5} + " + shift; shift; shift; shift; shift;; + --disk-image-size) + DISK_IMAGE_SIZE="$2" + shift; shift;; + --hdiutil-verbose) + HDIUTIL_VERBOSITY='-verbose' + shift;; + --hdiutil-quiet) + HDIUTIL_VERBOSITY='-quiet' + shift;; + --sandbox-safe) + SANDBOX_SAFE=1 + shift;; + --rez) + REZ_PATH="$2" + shift; shift;; + --skip-jenkins) + SKIP_JENKINS=1 + shift;; + -*) + echo "Unknown option: $1. Run 'create-dmg --help' for help." + exit 1;; + esac + case $FORMAT in + UDZO) + IMAGEKEY="-imagekey zlib-level=9";; + UDBZ) + IMAGEKEY="-imagekey bzip2-level=9";; + *) + echo >&2 "Unknown format: $FORMAT" + exit 1;; + esac +done + +if [[ -z "$2" ]]; then + echo "Not enough arguments. Run 'create-dmg --help' for help." + exit 1 +fi + DMG_PATH="$1" +SRC_FOLDER="$(cd "$2" > /dev/null; pwd)" + +# Main script logic + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" DMG_DIRNAME="$(dirname "$DMG_PATH")" DMG_DIR="$(cd "$DMG_DIRNAME" > /dev/null; pwd)" DMG_NAME="$(basename "$DMG_PATH")" DMG_TEMP_NAME="$DMG_DIR/rw.${DMG_NAME}" -SRC_FOLDER="$(cd "$2" > /dev/null; pwd)" -test -z "$VOLUME_NAME" && VOLUME_NAME="$(basename "$DMG_PATH" .dmg)" -AUX_PATH="$SCRIPT_DIR/support" +# Detect where we're running from -test -d "$AUX_PATH" || { - echo "Cannot find support directory: $AUX_PATH" - exit 1 -} +sentinel_file="$SCRIPT_DIR/.this-is-the-create-dmg-repo" +if [[ -f "$sentinel_file" ]]; then + # We're running from inside a repo + CDMG_SUPPORT_DIR="$SCRIPT_DIR/support" +else + # We're running inside an installed location + bin_dir="$SCRIPT_DIR" + prefix_dir=$(dirname "$bin_dir") + CDMG_SUPPORT_DIR="$prefix_dir/share/create-dmg/support" +fi -if [ -f "$SRC_FOLDER/.DS_Store" ]; then - echo "Deleting any .DS_Store in source folder" - rm "$SRC_FOLDER/.DS_Store" +if [[ -z "$VOLUME_NAME" ]]; then + VOLUME_NAME="$(basename "$DMG_PATH" .dmg)" +fi + +if [[ ! -d "$CDMG_SUPPORT_DIR" ]]; then + echo >&2 "Cannot find support/ directory: expected at: $CDMG_SUPPORT_DIR" + exit 1 +fi + +if [[ -f "$SRC_FOLDER/.DS_Store" ]]; then + echo "Deleting .DS_Store found in source folder" + rm "$SRC_FOLDER/.DS_Store" fi # Create the image echo "Creating disk image..." -test -f "${DMG_TEMP_NAME}" && rm -f "${DMG_TEMP_NAME}" -ACTUAL_SIZE=`du -sm "$SRC_FOLDER" | sed -e 's/ .*//g'` -DISK_IMAGE_SIZE=$(expr $ACTUAL_SIZE + 20) -hdiutil create -srcfolder "$SRC_FOLDER" -volname "${VOLUME_NAME}" -fs HFS+ -fsargs "-c c=64,a=16,e=16" -format UDRW -size ${DISK_IMAGE_SIZE}m "${DMG_TEMP_NAME}" +if [[ -f "${DMG_TEMP_NAME}" ]]; then + rm -f "${DMG_TEMP_NAME}" +fi + +# Use Megabytes since hdiutil fails with very large byte numbers +function blocks_to_megabytes() { + # Add 1 extra MB, since there's no decimal retention here + MB_SIZE=$((($1 * 512 / 1000 / 1000) + 1)) + echo $MB_SIZE +} + +function get_size() { + # Get block size in disk + bytes_size=$(du -s "$1" | sed -e 's/ .*//g') + echo $(blocks_to_megabytes $bytes_size) +} + +# Create the DMG with the specified size or the hdiutil estimation +CUSTOM_SIZE='' +if [[ -n "$DISK_IMAGE_SIZE" ]]; then + CUSTOM_SIZE="-size ${DISK_IMAGE_SIZE}m" +fi + +if [[ $SANDBOX_SAFE -eq 0 ]]; then + hdiutil create ${HDIUTIL_VERBOSITY} -srcfolder "$SRC_FOLDER" -volname "${VOLUME_NAME}" \ + -fs HFS+ -fsargs "-c c=64,a=16,e=16" -format UDRW ${CUSTOM_SIZE} "${DMG_TEMP_NAME}" +else + hdiutil makehybrid ${HDIUTIL_VERBOSITY} -default-volume-name "${VOLUME_NAME}" -hfs -o "${DMG_TEMP_NAME}" "$SRC_FOLDER" + hdiutil convert -format UDRW -ov -o "${DMG_TEMP_NAME}" "${DMG_TEMP_NAME}" + DISK_IMAGE_SIZE_CUSTOM=$DISK_IMAGE_SIZE +fi + +# Get the created DMG actual size +DISK_IMAGE_SIZE=$(get_size "${DMG_TEMP_NAME}") + +# Use the custom size if bigger +if [[ $SANDBOX_SAFE -eq 1 ]] && [[ ! -z "$DISK_IMAGE_SIZE_CUSTOM" ]] && [[ $DISK_IMAGE_SIZE_CUSTOM -gt $DISK_IMAGE_SIZE ]]; then + DISK_IMAGE_SIZE=$DISK_IMAGE_SIZE_CUSTOM +fi + +# Estimate the additional soruces size +if [[ -n "$ADD_FILE_SOURCES" ]]; then + for i in "${!ADD_FILE_SOURCES[@]}"; do + SOURCE_SIZE=$(get_size "${ADD_FILE_SOURCES[$i]}") + DISK_IMAGE_SIZE=$(expr $DISK_IMAGE_SIZE + $SOURCE_SIZE) + done +fi + +# Add extra space for additional resources +DISK_IMAGE_SIZE=$(expr $DISK_IMAGE_SIZE + 20) + +# Resize the image for the extra stuff +hdiutil resize ${HDIUTIL_VERBOSITY} -size ${DISK_IMAGE_SIZE}m "${DMG_TEMP_NAME}" + +# Mount the new DMG -# mount it -echo "Mounting disk image..." MOUNT_DIR="/Volumes/${VOLUME_NAME}" -# try unmount dmg if it was mounted previously (e.g. developer mounted dmg, installed app and forgot to unmount it) -echo "Unmounting disk image..." -DEV_NAME=$(hdiutil info | egrep '^/dev/' | sed 1q | awk '{print $1}') -test -d "${MOUNT_DIR}" && hdiutil detach "${DEV_NAME}" +# Unmount leftover dmg if it was mounted previously (e.g. developer mounted dmg, installed app and forgot to unmount it) +if [[ -d "${MOUNT_DIR}" ]]; then + echo "Unmounting old disk image from $MOUNT_DIR..." + DEV_NAME=$(hdiutil info | egrep --color=never '^/dev/' | sed 1q | awk '{print $1}') + hdiutil detach "${DEV_NAME}" +fi + +echo "Mounting disk image..." echo "Mount directory: $MOUNT_DIR" -DEV_NAME=$(hdiutil attach -readwrite -noverify -noautoopen "${DMG_TEMP_NAME}" | egrep '^/dev/' | sed 1q | awk '{print $1}') +DEV_NAME=$(hdiutil attach -readwrite -noverify -noautoopen "${DMG_TEMP_NAME}" | egrep --color=never '^/dev/' | sed 1q | awk '{print $1}') echo "Device name: $DEV_NAME" -if ! test -z "$BACKGROUND_FILE"; then - echo "Copying background file..." - test -d "$MOUNT_DIR/.background" || mkdir "$MOUNT_DIR/.background" - cp "$BACKGROUND_FILE" "$MOUNT_DIR/.background/$BACKGROUND_FILE_NAME" +if [[ -n "$BACKGROUND_FILE" ]]; then + echo "Copying background file..." + [[ -d "$MOUNT_DIR/.background" ]] || mkdir "$MOUNT_DIR/.background" + cp "$BACKGROUND_FILE" "$MOUNT_DIR/.background/$BACKGROUND_FILE_NAME" fi -if ! test -z "$APPLICATION_LINK"; then - echo "making link to Applications dir" - echo $MOUNT_DIR - ln -s /Applications "$MOUNT_DIR/Applications" +if [[ -n "$APPLICATION_LINK" ]]; then + echo "Making link to Applications dir..." + echo $MOUNT_DIR + ln -s /Applications "$MOUNT_DIR/Applications" fi -if ! test -z "$VOLUME_ICON_FILE"; then - echo "Copying volume icon file '$VOLUME_ICON_FILE'..." - cp "$VOLUME_ICON_FILE" "$MOUNT_DIR/.VolumeIcon.icns" - SetFile -c icnC "$MOUNT_DIR/.VolumeIcon.icns" +if [[ -n "$QL_LINK" ]]; then + echo "Making link to QuickLook install dir..." + echo $MOUNT_DIR + ln -s "/Library/QuickLook" "$MOUNT_DIR/QuickLook" fi -# run applescript -APPLESCRIPT=$(mktemp -t createdmg) -cat "$AUX_PATH/template.applescript" | sed -e "s/WINX/$WINX/g" -e "s/WINY/$WINY/g" -e "s/WINW/$WINW/g" -e "s/WINH/$WINH/g" -e "s/BACKGROUND_CLAUSE/$BACKGROUND_CLAUSE/g" -e "s/REPOSITION_HIDDEN_FILES_CLAUSE/$REPOSITION_HIDDEN_FILES_CLAUSE/g" -e "s/ICON_SIZE/$ICON_SIZE/g" -e "s/TEXT_SIZE/$TEXT_SIZE/g" | perl -pe "s/POSITION_CLAUSE/$POSITION_CLAUSE/g" | perl -pe "s/APPLICATION_CLAUSE/$APPLICATION_CLAUSE/g" | perl -pe "s/HIDING_CLAUSE/$HIDING_CLAUSE/" >"$APPLESCRIPT" +if [[ -n "$VOLUME_ICON_FILE" ]]; then + echo "Copying volume icon file '$VOLUME_ICON_FILE'..." + cp "$VOLUME_ICON_FILE" "$MOUNT_DIR/.VolumeIcon.icns" + SetFile -c icnC "$MOUNT_DIR/.VolumeIcon.icns" +fi -echo "Running Applescript: /usr/bin/osascript \"${APPLESCRIPT}\" \"${VOLUME_NAME}\"" -"/usr/bin/osascript" "${APPLESCRIPT}" "${VOLUME_NAME}" || true -echo "Done running the applescript..." -sleep 4 +if [[ -n "$ADD_FILE_SOURCES" ]]; then + echo "Copying custom files..." + for i in "${!ADD_FILE_SOURCES[@]}"; do + echo "${ADD_FILE_SOURCES[$i]}" + cp -a "${ADD_FILE_SOURCES[$i]}" "$MOUNT_DIR/${ADD_FILE_TARGETS[$i]}" + done +fi -rm "$APPLESCRIPT" +# Run AppleScript to do all the Finder cosmetic stuff +APPLESCRIPT_FILE=$(mktemp -t createdmg.tmp.XXXXXXXXXX) +if [[ $SANDBOX_SAFE -eq 1 ]]; then + echo "Skipping Finder-prettifying AppleScript because we are in Sandbox..." +else + if [[ $SKIP_JENKINS -eq 0 ]]; then + cat "$CDMG_SUPPORT_DIR/template.applescript" \ + | sed -e "s/WINX/$WINX/g" -e "s/WINY/$WINY/g" -e "s/WINW/$WINW/g" \ + -e "s/WINH/$WINH/g" -e "s/BACKGROUND_CLAUSE/$BACKGROUND_CLAUSE/g" \ + -e "s/REPOSITION_HIDDEN_FILES_CLAUSE/$REPOSITION_HIDDEN_FILES_CLAUSE/g" \ + -e "s/ICON_SIZE/$ICON_SIZE/g" -e "s/TEXT_SIZE/$TEXT_SIZE/g" \ + | perl -pe "s/POSITION_CLAUSE/$POSITION_CLAUSE/g" \ + | perl -pe "s/QL_CLAUSE/$QL_CLAUSE/g" \ + | perl -pe "s/APPLICATION_CLAUSE/$APPLICATION_CLAUSE/g" \ + | perl -pe "s/HIDING_CLAUSE/$HIDING_CLAUSE/" \ + > "$APPLESCRIPT_FILE" + sleep 2 # pause to workaround occasional "Can’t get disk" (-1728) issues + echo "Running AppleScript to make Finder stuff pretty: /usr/bin/osascript \"${APPLESCRIPT_FILE}\" \"${VOLUME_NAME}\"" + if /usr/bin/osascript "${APPLESCRIPT_FILE}" "${VOLUME_NAME}"; then + # Okay, we're cool + true + else + echo >&2 "Failed running AppleScript" + hdiutil detach "${DEV_NAME}" + exit 64 + fi + echo "Done running the AppleScript..." + sleep 4 + rm "$APPLESCRIPT_FILE" + fi +fi -# make sure it's not world writeable +# Make sure it's not world writeable echo "Fixing permissions..." chmod -Rf go-w "${MOUNT_DIR}" &> /dev/null || true -echo "Done fixing permissions." +echo "Done fixing permissions" -# make the top window open itself on mount: -echo "Blessing started" -bless --folder "${MOUNT_DIR}" --openfolder "${MOUNT_DIR}" -echo "Blessing finished" - -if ! test -z "$VOLUME_ICON_FILE"; then - # tell the volume that it has a special file attribute - SetFile -a C "$MOUNT_DIR" +# Make the top window open itself on mount: +if [[ $SANDBOX_SAFE -eq 0 ]]; then + echo "Blessing started" + bless --folder "${MOUNT_DIR}" --openfolder "${MOUNT_DIR}" + echo "Blessing finished" +else + echo "Skipping blessing on sandbox" fi -# unmount +if [[ -n "$VOLUME_ICON_FILE" ]]; then + # Tell the volume that it has a special file attribute + SetFile -a C "$MOUNT_DIR" +fi + +# Unmount echo "Unmounting disk image..." hdiutil detach "${DEV_NAME}" -# compress image +# Compress image echo "Compressing disk image..." -hdiutil convert "${DMG_TEMP_NAME}" -format UDZO -imagekey zlib-level=9 -o "${DMG_DIR}/${DMG_NAME}" +hdiutil convert ${HDIUTIL_VERBOSITY} "${DMG_TEMP_NAME}" -format ${FORMAT} ${IMAGEKEY} -o "${DMG_DIR}/${DMG_NAME}" rm -f "${DMG_TEMP_NAME}" -# adding EULA resources -if [ ! -z "${EULA_RSRC}" -a "${EULA_RSRC}" != "-null-" ]; then - echo "adding EULA resources" - "${AUX_PATH}/dmg-license.py" "${DMG_DIR}/${DMG_NAME}" "${EULA_RSRC}" +# Adding EULA resources +if [[ -n "${EULA_RSRC}" && "${EULA_RSRC}" != "-null-" ]]; then + echo "Adding EULA resources..." + REZ_ARG="" + if [[ -n "${REZ_PATH}" && "${REZ_PATH}" != "-null-" ]]; then + REZ_ARG="--rez ${REZ_PATH}" + else + xcode_path=$(xcode-select -p) + default_rez="$xcode_path/Tools/Rez" + REZ_ARG="--rez ${default_rez}" + fi + "${CDMG_SUPPORT_DIR}/licenseDMG.py" "${DMG_DIR}/${DMG_NAME}" "${EULA_RSRC}" ${REZ_ARG} fi -if [ ! -z "${NOINTERNET}" -a "${NOINTERNET}" == 1 ]; then - echo "not setting 'internet-enable' on the dmg" +# Enable "internet", whatever that is +if [[ ! -z "${NOINTERNET}" && "${NOINTERNET}" == 1 ]]; then + echo "Not setting 'internet-enable' on the dmg, per caller request" else - hdiutil internet-enable -yes "${DMG_DIR}/${DMG_NAME}" + # Check if hdiutil supports internet-enable + # Support was removed in macOS 10.15. See https://github.com/andreyvit/create-dmg/issues/76 + if hdiutil internet-enable -help >/dev/null 2>/dev/null; then + hdiutil internet-enable -yes "${DMG_DIR}/${DMG_NAME}" + else + echo "hdiutil does not support internet-enable. Note it was removed in macOS 10.15." + fi fi +# All done! echo "Disk image done" exit 0 diff --git a/src/create-dmg/doc-project/Developer Notes.md b/src/create-dmg/doc-project/Developer Notes.md new file mode 100644 index 0000000000..892c0e54c0 --- /dev/null +++ b/src/create-dmg/doc-project/Developer Notes.md @@ -0,0 +1,35 @@ +# create-dmg Developer Notes + +## Repo layout + +- `create-dmg` in the root of the repo is the main program +- `support/` contains auxiliary scripts used by `create-dmg`; it must be at that relative position to `create-dmg` +- `builder/` contains ???? +- `examples/` contains user-facing examples +- `tests/` contains regression tests for developers +- `doc-project/` contains developer-facing documentation about this project + +### tests/ + +The `tests/` folder contains regression tests for developers. + +Each test is in its own subfolder. +Each subfolder name should start with a 3-digit number that is the number of the corresponding bug report in create-dmg's GitHub issue tracker. + +The tests are to be run manually, with the results examined manually. +There's no automated script to run them as a suite and check their results. +That might be nice to have. + +### examples/ + +Each example is in its own subfolder. +The subfolder prefix number is arbitrary; these numbers should roughly be in order of "advancedness" of examples, so it makes sense for users to go through them in order. + +## Versioning + +As of May 2020, we're using SemVer versioning. +The old version numbers were 4-parters, like "1.0.0.7". +Now we use 3-part SemVer versions, like "1.0.8". +This change happened after version 1.0.0.7; 1.0.8 is the next release after 1.0.0.7. + +The suffix "-SNAPSHOT" is used to denote a version that is still under development. diff --git a/src/create-dmg/doc-project/Release Checklist.md b/src/create-dmg/doc-project/Release Checklist.md new file mode 100644 index 0000000000..508392075e --- /dev/null +++ b/src/create-dmg/doc-project/Release Checklist.md @@ -0,0 +1,10 @@ +# Release Checklist + +- Update the version in `create-dmg`'s `pure_version` function + - Remove the "-SNAPSHOT" suffix +- Commit +- Tag the release as `vX.X.X` +- `git push --tags` +- Create a release on the GitHub project page +- Open development on the next release + - Bump the version number and add a "-SNAPSHOT" suffix to it diff --git a/src/create-dmg/examples/01-main-example/installer_background.png b/src/create-dmg/examples/01-main-example/installer_background.png new file mode 100644 index 0000000000000000000000000000000000000000..bdfa5ed0d86bc613a0c327c1abaaa86005a8c25f GIT binary patch literal 35656 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%0Datr0rJ< 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 Ppr5#~j8K)pZ~y-XvR=-r literal 0 HcmV?d00001 diff --git a/src/create-dmg/examples/01-main-example/sample b/src/create-dmg/examples/01-main-example/sample new file mode 100755 index 0000000000..c18a65bb05 --- /dev/null +++ b/src/create-dmg/examples/01-main-example/sample @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +if [[ -e ../../create-dmg ]]; then + # We're running from the repo + CREATE_DMG=../../create-dmg +else + # We're running from an installation under a prefix + CREATE_DMG=../../../../bin/create-dmg +fi + +# Since create-dmg does not clobber, be sure to delete previous DMG +[[ -f Application-Installer.dmg ]] && rm Application-Installer.dmg + +# Create the DMG +$CREATE_DMG \ + --volname "Application Installer" \ + --background "installer_background.png" \ + --window-pos 200 120 \ + --window-size 800 400 \ + --icon-size 100 \ + --icon "Application.app" 200 190 \ + --hide-extension "Application.app" \ + --app-drop-link 600 185 \ + "Application-Installer.dmg" \ + "source_folder/" diff --git a/src/create-dmg/examples/01-main-example/source_folder/Application.app b/src/create-dmg/examples/01-main-example/source_folder/Application.app new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/create-dmg/support/LICENSE-licenseDMG-pyhacker b/src/create-dmg/support/LICENSE-licenseDMG-pyhacker new file mode 100644 index 0000000000..94b7fe934e --- /dev/null +++ b/src/create-dmg/support/LICENSE-licenseDMG-pyhacker @@ -0,0 +1,19 @@ +Copyright (c) 2015-2019 Jared Hobbs + +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. \ No newline at end of file diff --git a/src/create-dmg/support/README b/src/create-dmg/support/README new file mode 100644 index 0000000000..0ecb9eefce --- /dev/null +++ b/src/create-dmg/support/README @@ -0,0 +1,7 @@ +# create-dmg support files + +## licenseDMG.py + +The licenseDMG.py script is taken from Jared Hobbs' pyhacker library, at . + +See LICENSE-licenseDMG-pyhacker for licensing details. \ No newline at end of file diff --git a/src/create-dmg/support/dmg-license.py b/src/create-dmg/support/dmg-license.py deleted file mode 100755 index 9003a7c5e7..0000000000 --- a/src/create-dmg/support/dmg-license.py +++ /dev/null @@ -1,163 +0,0 @@ -#! /usr/bin/env python -""" -This script adds a license file to a DMG. Requires Xcode and a plain ascii text -license file. -Obviously only runs on a Mac. - -Copyright (C) 2011-2013 Jared Hobbs - -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. -""" -import os -import sys -import tempfile -import optparse - - -class Path(str): - def __enter__(self): - return self - - def __exit__(self, type, value, traceback): - os.unlink(self) - - -def mktemp(dir=None, suffix=''): - (fd, filename) = tempfile.mkstemp(dir=dir, suffix=suffix) - os.close(fd) - return Path(filename) - - -def main(options, args): - dmgFile, license = args - with mktemp('.') as tmpFile: - with open(tmpFile, 'w') as f: - f.write("""data 'TMPL' (128, "LPic") { - $"1344 6566 6175 6C74 204C 616E 6775 6167" - $"6520 4944 4457 5244 0543 6F75 6E74 4F43" - $"4E54 042A 2A2A 2A4C 5354 430B 7379 7320" - $"6C61 6E67 2049 4444 5752 441E 6C6F 6361" - $"6C20 7265 7320 4944 2028 6F66 6673 6574" - $"2066 726F 6D20 3530 3030 4457 5244 1032" - $"2D62 7974 6520 6C61 6E67 7561 6765 3F44" - $"5752 4404 2A2A 2A2A 4C53 5445" -}; - -data 'LPic' (5000) { - $"0000 0002 0000 0000 0000 0000 0004 0000" -}; - -data 'STR#' (5000, "English buttons") { - $"0006 0D45 6E67 6C69 7368 2074 6573 7431" - $"0541 6772 6565 0844 6973 6167 7265 6505" - $"5072 696E 7407 5361 7665 2E2E 2E7A 4966" - $"2079 6F75 2061 6772 6565 2077 6974 6820" - $"7468 6520 7465 726D 7320 6F66 2074 6869" - $"7320 6C69 6365 6E73 652C 2063 6C69 636B" - $"2022 4167 7265 6522 2074 6F20 6163 6365" - $"7373 2074 6865 2073 6F66 7477 6172 652E" - $"2020 4966 2079 6F75 2064 6F20 6E6F 7420" - $"6167 7265 652C 2070 7265 7373 2022 4469" - $"7361 6772 6565 2E22" -}; - -data 'STR#' (5002, "English") { - $"0006 0745 6E67 6C69 7368 0541 6772 6565" - $"0844 6973 6167 7265 6505 5072 696E 7407" - $"5361 7665 2E2E 2E7B 4966 2079 6F75 2061" - $"6772 6565 2077 6974 6820 7468 6520 7465" - $"726D 7320 6F66 2074 6869 7320 6C69 6365" - $"6E73 652C 2070 7265 7373 2022 4167 7265" - $"6522 2074 6F20 696E 7374 616C 6C20 7468" - $"6520 736F 6674 7761 7265 2E20 2049 6620" - $"796F 7520 646F 206E 6F74 2061 6772 6565" - $"2C20 7072 6573 7320 2244 6973 6167 7265" - $"6522 2E" -};\n\n""") - with open(license, 'r') as l: - kind = 'RTF ' if license.lower().endswith('.rtf') else 'TEXT' - f.write('data \'%s\' (5000, "English") {\n' % kind) - def escape(s): - return s.strip().replace('\\', '\\\\').replace('"', '\\"') - - for line in l: - if len(line) < 1000: - f.write(' "' + escape(line) + '\\n"\n') - else: - for liner in line.split('.'): - f.write(' "' + escape(liner) + '. \\n"\n') - f.write('};\n\n') - f.write("""data 'styl' (5000, "English") { - $"0003 0000 0000 000C 0009 0014 0000 0000" - $"0000 0000 0000 0000 0027 000C 0009 0014" - $"0100 0000 0000 0000 0000 0000 002A 000C" - $"0009 0014 0000 0000 0000 0000 0000" -};\n""") - os.system('hdiutil unflatten -quiet "%s"' % dmgFile) - ret = os.system('%s -a %s -o "%s"' % - (options.rez, tmpFile, dmgFile)) - os.system('hdiutil flatten -quiet "%s"' % dmgFile) - if options.compression is not None: - os.system('cp %s %s.temp.dmg' % (dmgFile, dmgFile)) - os.remove(dmgFile) - if options.compression == "bz2": - os.system('hdiutil convert %s.temp.dmg -format UDBZ -o %s' % - (dmgFile, dmgFile)) - elif options.compression == "gz": - os.system('hdiutil convert %s.temp.dmg -format ' % dmgFile + - 'UDZO -imagekey zlib-devel=9 -o %s' % dmgFile) - os.remove('%s.temp.dmg' % dmgFile) - if ret == 0: - print "Successfully added license to '%s'" % dmgFile - else: - print "Failed to add license to '%s'" % dmgFile - -if __name__ == '__main__': - parser = optparse.OptionParser() - parser.set_usage("""%prog [OPTIONS] - This program adds a software license agreement to a DMG file. - It requires Xcode and either a plain ascii text - or a with the RTF contents. - - See --help for more details.""") - parser.add_option( - '--rez', - '-r', - action='store', - default='/Applications/Xcode.app/Contents/Developer/Tools/Rez', - help='The path to the Rez tool. Defaults to %default' - ) - parser.add_option( - '--compression', - '-c', - action='store', - choices=['bz2', 'gz'], - default=None, - help='Optionally compress dmg using specified compression type. ' - 'Choices are bz2 and gz.' - ) - options, args = parser.parse_args() - cond = len(args) != 2 - if not os.path.exists(options.rez): - print 'Failed to find Rez at "%s"!\n' % options.rez - cond = True - if cond: - parser.print_usage() - sys.exit(1) - main(options, args) diff --git a/src/create-dmg/support/licenseDMG.py b/src/create-dmg/support/licenseDMG.py new file mode 100755 index 0000000000..f6eaf98395 --- /dev/null +++ b/src/create-dmg/support/licenseDMG.py @@ -0,0 +1,200 @@ +#! /usr/bin/env python +""" +This script adds a license file to a DMG. Requires Xcode and a plain ascii text +license file or an RTF license file. +Obviously only runs on a Mac. + +Copyright (C) 2011-2019 Jared Hobbs + +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. +""" +from __future__ import unicode_literals +from subprocess import check_call, check_output, call, CalledProcessError +import argparse +import logging as logger +import os +import sys +import tempfile + +logger.basicConfig(format='%(message)s', level=logger.DEBUG) + + +class Path(str): + def __enter__(self): + return self + + def __exit__(self, type, value, traceback): + os.unlink(self) + + +def mktemp(dir=None, suffix=''): + fd, filename = tempfile.mkstemp(dir=dir, suffix=suffix) + os.close(fd) + return Path(filename) + + +def escape(s): + return s.strip().replace('\\', '\\\\').replace('"', '\\"') + + +def main(options): + dmg_file = options.dmg_file + output = options.output or dmg_file + license = options.license_file + if dmg_file != output: + check_call(['cp', dmg_file, output]) + dmg_file = output + with mktemp('.') as tmp_file: + with open(tmp_file, 'w') as f: + f.write("""\ +data 'TMPL' (128, "LPic") { + $"1344 6566 6175 6C74 204C 616E 6775 6167" + $"6520 4944 4457 5244 0543 6F75 6E74 4F43" + $"4E54 042A 2A2A 2A4C 5354 430B 7379 7320" + $"6C61 6E67 2049 4444 5752 441E 6C6F 6361" + $"6C20 7265 7320 4944 2028 6F66 6673 6574" + $"2066 726F 6D20 3530 3030 4457 5244 1032" + $"2D62 7974 6520 6C61 6E67 7561 6765 3F44" + $"5752 4404 2A2A 2A2A 4C53 5445" +}; + +data 'LPic' (5000) { + $"0000 0002 0000 0000 0000 0000 0004 0000" +}; + +data 'STR#' (5000, "English buttons") { + $"0006 0D45 6E67 6C69 7368 2074 6573 7431" + $"0541 6772 6565 0844 6973 6167 7265 6505" + $"5072 696E 7407 5361 7665 2E2E 2E7A 4966" + $"2079 6F75 2061 6772 6565 2077 6974 6820" + $"7468 6520 7465 726D 7320 6F66 2074 6869" + $"7320 6C69 6365 6E73 652C 2063 6C69 636B" + $"2022 4167 7265 6522 2074 6F20 6163 6365" + $"7373 2074 6865 2073 6F66 7477 6172 652E" + $"2020 4966 2079 6F75 2064 6F20 6E6F 7420" + $"6167 7265 652C 2070 7265 7373 2022 4469" + $"7361 6772 6565 2E22" +}; + +data 'STR#' (5002, "English") { + $"0006 0745 6E67 6C69 7368 0541 6772 6565" + $"0844 6973 6167 7265 6505 5072 696E 7407" + $"5361 7665 2E2E 2E7B 4966 2079 6F75 2061" + $"6772 6565 2077 6974 6820 7468 6520 7465" + $"726D 7320 6F66 2074 6869 7320 6C69 6365" + $"6E73 652C 2070 7265 7373 2022 4167 7265" + $"6522 2074 6F20 696E 7374 616C 6C20 7468" + $"6520 736F 6674 7761 7265 2E20 2049 6620" + $"796F 7520 646F 206E 6F74 2061 6772 6565" + $"2C20 7072 6573 7320 2244 6973 6167 7265" + $"6522 2E" +};\n\n""") + with open(license, 'r') as l_file: + kind = 'RTF ' if license.lower().endswith('.rtf') else 'TEXT' + f.write('data \'{}\' (5000, "English") {{\n'.format(kind)) + + for line in l_file: + if len(line) < 1000: + f.write(' "{}\\n"\n'.format(escape(line))) + else: + for liner in line.split('.'): + f.write(' "{}. \\n"\n'.format(escape(liner))) + f.write('};\n\n') + f.write("""\ +data 'styl' (5000, "English") { + $"0003 0000 0000 000C 0009 0014 0000 0000" + $"0000 0000 0000 0000 0027 000C 0009 0014" + $"0100 0000 0000 0000 0000 0000 002A 000C" + $"0009 0014 0000 0000 0000 0000 0000" +};\n""") + call(['hdiutil', 'unflatten', '-quiet', dmg_file]) + ret = check_call([options.rez, '-a', tmp_file, '-o', dmg_file]) + call(['hdiutil', 'flatten', '-quiet', dmg_file]) + if options.compression is not None: + tmp_dmg = '{}.temp.dmg'.format(dmg_file) + check_call(['cp', dmg_file, tmp_dmg]) + os.remove(dmg_file) + args = ['hdiutil', 'convert', tmp_dmg, '-quiet', '-format'] + if options.compression == 'bz2': + args.append('UDBZ') + elif options.compression == "gz": + args.extend(['UDZO', '-imagekey', 'zlib-devel=9']) + args.extend(['-o', dmg_file]) + check_call(args) + os.remove(tmp_dmg) + if ret == 0: + logger.info("Successfully added license to '{}'".format(dmg_file)) + else: + logger.error("Failed to add license to '{}'".format(dmg_file)) + + +if __name__ == '__main__': + try: + rez_path = check_output( + ['xcrun', '--find', 'Rez'], + ).strip().decode('utf-8') + except CalledProcessError: + rez_path = '/Library/Developer/CommandLineTools/usr/bin/Rez' + parser = argparse.ArgumentParser( + description="""\ +This program adds a software license agreement to a DMG file. +It requires Xcode and either a plain ascii text +or a with the RTF contents. + +See --help for more details.""", + ) + parser.add_argument( + 'dmg_file', + help='the path to the dmg file which will receive the license', + ) + parser.add_argument( + 'license_file', + help='the path to the plain ascii or RTF license file; for RTF files, ' + 'the file must use a .rtf extension', + ) + parser.add_argument( + '--rez', + '-r', + action='store', + default=rez_path, + help='the path to the Rez tool; defaults to %(default)s', + ) + parser.add_argument( + '--compression', + '-c', + action='store', + choices=('bz2', 'gz'), + default=None, + help='optionally compress dmg using specified compression type; ' + 'choices are bz2 and gz', + ) + parser.add_argument( + '--output', + '-o', + action='store', + default=None, + help='specify an output DMG file; if not given, the license will be ' + 'directly applied to the input DMG file', + ) + options = parser.parse_args() + if not os.path.exists(options.rez): + logger.error('Failed to find Rez at "{}"!\n'.format(options.rez)) + parser.print_usage() + sys.exit(1) + main(options) diff --git a/src/create-dmg/support/template.applescript b/src/create-dmg/support/template.applescript index 2a7f2aa0b6..fee634d319 100644 --- a/src/create-dmg/support/template.applescript +++ b/src/create-dmg/support/template.applescript @@ -2,16 +2,16 @@ on run (volumeName) tell application "Finder" tell disk (volumeName as string) open - + set theXOrigin to WINX set theYOrigin to WINY set theWidth to WINW set theHeight to WINH - + set theBottomRightX to (theXOrigin + theWidth) set theBottomRightY to (theYOrigin + theHeight) set dsStore to "\"" & "/Volumes/" & volumeName & "/" & ".DS_STORE\"" - + tell container window set current view to icon view set toolbar visible to false @@ -20,7 +20,7 @@ on run (volumeName) set statusbar visible to false REPOSITION_HIDDEN_FILES_CLAUSE end tell - + set opts to the icon view options of container window tell opts set icon size to ICON_SIZE @@ -28,44 +28,39 @@ on run (volumeName) set arrangement to not arranged end tell BACKGROUND_CLAUSE - + -- Positioning POSITION_CLAUSE - + -- Hiding HIDING_CLAUSE - - -- Application Link Clause + + -- Application and QL Link Clauses APPLICATION_CLAUSE - close - open - - update without registering applications + QL_CLAUSE + close + open -- Force saving of the size delay 1 - + tell container window set statusbar visible to false set the bounds to {theXOrigin, theYOrigin, theBottomRightX - 10, theBottomRightY - 10} end tell - - update without registering applications end tell - + delay 1 - + tell disk (volumeName as string) tell container window set statusbar visible to false set the bounds to {theXOrigin, theYOrigin, theBottomRightX, theBottomRightY} end tell - - update without registering applications end tell - + --give the finder some time to write the .DS_Store file delay 3 - + set waitTime to 0 set ejectMe to false repeat while ejectMe is false diff --git a/src/create-dmg/tests/007-space-in-dir-name/my files/hello.txt b/src/create-dmg/tests/007-space-in-dir-name/my files/hello.txt new file mode 100644 index 0000000000..802992c422 --- /dev/null +++ b/src/create-dmg/tests/007-space-in-dir-name/my files/hello.txt @@ -0,0 +1 @@ +Hello world diff --git a/src/create-dmg/tests/007-space-in-dir-name/run-test b/src/create-dmg/tests/007-space-in-dir-name/run-test new file mode 100755 index 0000000000..ab7f96ab42 --- /dev/null +++ b/src/create-dmg/tests/007-space-in-dir-name/run-test @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +# Test for https://github.com/create-dmg/create-dmg/issues/7 - spaces in folder names + +../../create-dmg "my disk image.dmg" "my files" \ No newline at end of file