macOS script to build the universal app bundle.

1. Build TuxPaint.app on Intel macOS, rename it TuxPaint-x86_64.app.
2. Build TuxPaint.app on Apple Silicon macOS, rename it TuxPaint-arm64.app.
3. Copy TuxPaint-x86_64.app to the same directory as TuxPaint-arm64.app
   on the Apple Silicon macOS, then run:

     macos/build-universal.sh TuxPaint-x86_64.app TuxPaint-arm64.app

   ... to produce TuxPaint.app that's universal.
4. `make TuxPaint.dmg` to produce TuxPaint.dmg from the universal
   TuxPaint.app.
This commit is contained in:
Mark Kim 2022-01-20 21:28:59 -05:00
parent c239e849cb
commit e5faf36d5d

142
macos/build-universal.sh Executable file
View file

@ -0,0 +1,142 @@
#!/usr/bin/env bash
function usage() {
cat <<EOF
Build a macOS universal application bundle from one or more application
bundles.
Usage: ${SCRIPTNAME} [OPTIONS] TuxPaint-1.app TuxPaint-2.app ...
TuxPaint-N.app Bundle(s) to read. If no bundles are specified, the
following files are read:
$(
for bundle in "${BUNDLES[@]}"; do
printf "\n%26s%s" "" "$bundle"
done
)
-o OUTBUNDLE Bundle to create. [Default=${OUTBUNDLE}]
EOF
}
##############################################################################
# CONFIG
ARCHS=( arm64 x86_64 )
BUNDLES=( $(printf "TuxPaint-%s.app\n" "${ARCHS[@]}") )
OUTBUNDLE=TuxPaint.app
FORCE=0
##############################################################################
# PROGRAM BEGINS HERE
function main() {
local OPTIND OPTARG OPTERR opt
local bundle
local isok=1
# Process arguments
while getopts "fo:h" opt; do
case "$opt" in
f) FORCE=1 ;;
o) OUTBUNDLE=$OPTARG ;;
h) usage && exit 0 ;;
*) isok=0
esac
done
shift $((OPTIND-1))
# Use the specified bundles
if (( $# )); then
BUNDLES=( "$@" )
fi
# Sanity check
if (( ! isok )); then
usage 1>&2
exit 1
fi
# Status
echo " * Creating universal app bundle $OUTBUNDLE by combining ${BUNDLES[*]}..."
# Validate input
for bundle in "${BUNDLES[@]}"; do
if ! [[ -d "$bundle" ]]; then
printf " -> FAILED: No such input bundle named %s exists\n" "$bundle" 1>&2
isok=0
fi
done
(( isok)) || exit 1
# Validate output
if [[ -d "$OUTBUNDLE" ]] && (( FORCE )); then
printf " -> Deleting %s...\n" "$OUTBUNDLE"
rm -rf "$OUTBUNDLE" || exitcode=1
elif [[ -d "$OUTBUNDLE" ]]; then
printf " -> FAILED: Output bundled named %s already exists, use -f option to recreate.\n" "$OUTBUNDLE" 1>&2
isok=0
fi
(( isok )) || exit 1
build-universal
}
function build-universal() {
local isok=1
local i j
# Create the base package without binaries or libraries
echo " -> Create skeleton app bundle ${OUTBUNDLE}..." \
&& cp -a "${BUNDLES[0]}" "$OUTBUNDLE" \
&& rm -f "${OUTBUNDLE}/Contents/MacOS"/* \
&& find "$OUTBUNDLE" -name '*.dylib' -print0 | xargs -0 rm -f \
|| return 1
# Create the universal binary for each binary and library
for (( i=0; i < ${#BUNDLES[@]}; i++ )); do
local filelist=()
local file
# Binaries
for file in "${BUNDLES[i]}/Contents/MacOS"/*; do
filelist+=( "${file#${BUNDLES[i]}/}" )
done
# Dynamic Libraries
while IFS= read -r file; do
filelist+=( "${file#${BUNDLES[i]}/}" )
done < <(find "${BUNDLES[i]}" -name '*.dylib')
# Build each as universal
for file in "${filelist[@]}"; do
local instances=()
# Skip if already built
[[ -e "${OUTBUNDLE}/${file}" ]] && continue
# Get this file's instances in all bundles
for (( j=i; j < ${#BUNDLES[@]}; j++ )); do
if [[ -e "${BUNDLES[j]}/${file}" ]]; then
instances+=( "${BUNDLES[j]}/${file}" )
fi
done
# Build into $OUTBUNDLE
echo " -> Combine ${file}..."
lipo -create -output "${OUTBUNDLE}/${file}" "${instances[@]}" || isok=0
done
done
(( isok ))
}
##############################################################################
# ENTRY POINT
main "$@"