1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
|
#!/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}]
-s IDENTITY Re-sign the bundle with IDENTITY. [Default=${IDENTITY}]
EOF
}
##############################################################################
# CONFIG
ARCHS=( arm64 x86_64 )
BUNDLES=( $(printf "TuxPaint-%s.app\n" "${ARCHS[@]}") )
OUTBUNDLE=TuxPaint.app
IDENTITY="-"
FORCE=0
##############################################################################
# PROGRAM BEGINS HERE
function main() {
local OPTIND OPTARG OPTERR opt
local bundle
local isok=1
# Process arguments
while getopts "fo:s:h" opt; do
case "$opt" in
f) FORCE=1 ;;
o) OUTBUNDLE=$OPTARG ;;
s) IDENTITY=$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
resign-bundle
}
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 version of 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 ))
}
function resign-bundle() {
echo " * Sign $OUTBUNDLE..."
codesign --remove-signature "$OUTBUNDLE"
codesign -s "$IDENTITY" "$OUTBUNDLE"
echo " -> Done!"
}
##############################################################################
# ENTRY POINT
main "$@"
|