#!/usr/bin/env bash # Script to produce an OS X installer .pkg and .app(.zip) usage() { echo "Build macOS packages, optionally signing and notarizing them." echo "Usage: $0 options" echo "Options:" echo " -s Enables code signing" echo " -f <APP_KEY.p12> Path to .p12 file for application signing" echo " -i <INSTALLER_KEY.p12> Path to .p12 file for installer signing" echo " -p <PASSWORD> Password for the .p12 files (necessary to access the certificates)" echo " -e <entitlements file> (Optional) Path to an entitlements XML file" echo " -n Enables notarization. This will fail if code signing is not also enabled." echo " -j <API_KEY.JSON> Path to JSON file generated with `rcodesign encode-app-store-connect-api-key` (required for notarization)" echo exit 1 } set -x set -e SIGN= NOTARIZE= ARM64_DEPLOY_TARGET='MACOSX_DEPLOYMENT_TARGET=11.0' X86_64_DEPLOY_TARGET='MACOSX_DEPLOYMENT_TARGET=10.9' # As of this writing, the most recent Rust release supports macOS back to 10.12. # The first supported version of macOS on arm64 is 10.15, so any Rust is fine for arm64. # We wish to support back to 10.9 on x86-64; the last version of Rust to support that is # version 1.73.0. RUST_VERSION_X86_64=1.73.0 while getopts "sf:i:p:e:nj:" opt; do case $opt in s) SIGN=1;; f) P12_APP_FILE=$(realpath "$OPTARG");; i) P12_INSTALL_FILE=$(realpath "$OPTARG");; p) P12_PASSWORD="$OPTARG";; e) ENTITLEMENTS_FILE=$(realpath "$OPTARG");; n) NOTARIZE=1;; j) API_KEY_FILE=$(realpath "$OPTARG");; \?) usage;; esac done if [ -n "$SIGN" ] && ([ -z "$P12_APP_FILE" ] || [-z "$P12_INSTALL_FILE"] || [ -z "$P12_PASSWORD" ]); then usage fi if [ -n "$NOTARIZE" ] && [ -z "$API_KEY_FILE" ]; then usage fi VERSION=$(git describe --always --dirty 2>/dev/null) if test -z "$VERSION" ; then echo "Could not get version from git" if test -f version; then VERSION=$(cat version) fi fi echo "Version is $VERSION" PKGDIR=$(mktemp -d) echo "$PKGDIR" SRC_DIR=$PWD OUTPUT_PATH=${FISH_ARTEFACT_PATH:-~/fish_built} mkdir -p "$PKGDIR/build_x86_64" "$PKGDIR/build_arm64" "$PKGDIR/root" "$PKGDIR/intermediates" "$PKGDIR/dst" # Build and install for arm64. # Pass FISH_USE_SYSTEM_PCRE2=OFF because a system PCRE2 on macOS will not be signed by fish, # and will probably not be built universal, so the package will fail to validate/run on other systems. # Note CMAKE_OSX_ARCHITECTURES is still relevant for the Mac app. { cd "$PKGDIR/build_arm64" \ && cmake \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DCMAKE_EXE_LINKER_FLAGS="-Wl,-ld_classic" \ -DWITH_GETTEXT=OFF \ -DRust_CARGO_TARGET=aarch64-apple-darwin \ -DCMAKE_OSX_ARCHITECTURES='arm64;x86_64' \ -DFISH_USE_SYSTEM_PCRE2=OFF \ "$SRC_DIR" \ && env $ARM64_DEPLOY_TARGET make VERBOSE=1 -j 12 \ && env DESTDIR="$PKGDIR/root/" $ARM64_DEPLOY_TARGET make install; } # Build for x86-64 but do not install; instead we will make some fat binaries inside the root. # Set RUST_VERSION_X86_64 to the last version of Rust that supports macOS 10.9. { cd "$PKGDIR/build_x86_64" \ && cmake \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DCMAKE_EXE_LINKER_FLAGS="-Wl,-ld_classic" \ -DWITH_GETTEXT=OFF \ -DRust_TOOLCHAIN="$RUST_VERSION_X86_64" \ -DRust_CARGO_TARGET=x86_64-apple-darwin \ -DCMAKE_OSX_ARCHITECTURES='arm64;x86_64' \ -DFISH_USE_SYSTEM_PCRE2=OFF "$SRC_DIR" \ && env $X86_64_DEPLOY_TARGET make VERBOSE=1 -j 12; } # Fatten them up. for FILE in "$PKGDIR"/root/usr/local/bin/*; do X86_FILE="$PKGDIR/build_x86_64/$(basename $FILE)" rcodesign macho-universal-create --output "$FILE" "$FILE" "$X86_FILE" chmod 755 "$FILE" done if test -n "$SIGN"; then echo "Signing executables" ARGS=( --p12-file "$P12_APP_FILE" --p12-password "$P12_PASSWORD" --code-signature-flags runtime --for-notarization ) if [ -n "$ENTITLEMENTS_FILE" ]; then ARGS+=(--entitlements-xml-file "$ENTITLEMENTS_FILE") fi for FILE in "$PKGDIR"/root/usr/local/bin/*; do (set +x; rcodesign sign "${ARGS[@]}" "$FILE") done fi pkgbuild --scripts "$SRC_DIR/build_tools/osx_package_scripts" --root "$PKGDIR/root/" --identifier 'com.ridiculousfish.fish-shell-pkg' --version "$VERSION" "$PKGDIR/intermediates/fish.pkg" productbuild --package-path "$PKGDIR/intermediates" --distribution "$SRC_DIR/build_tools/osx_distribution.xml" --resources "$SRC_DIR/build_tools/osx_package_resources/" "$OUTPUT_PATH/fish-$VERSION.pkg" if test -n "$SIGN"; then echo "Signing installer" ARGS=( --p12-file "$P12_INSTALL_FILE" --p12-password "$P12_PASSWORD" --code-signature-flags runtime --for-notarization ) (set +x; rcodesign sign "${ARGS[@]}" "$OUTPUT_PATH/fish-$VERSION.pkg") fi # Make the app (cd "$PKGDIR/build_arm64" && env $ARM64_DEPLOY_TARGET make -j 12 fish_macapp) (cd "$PKGDIR/build_x86_64" && env $X86_64_DEPLOY_TARGET make -j 12 fish_macapp) # Make the app's /usr/local/bin binaries universal. Note fish.app/Contents/MacOS/fish already is, courtesy of CMake. cd "$PKGDIR/build_arm64" for FILE in fish.app/Contents/Resources/base/usr/local/bin/*; do X86_FILE="$PKGDIR/build_x86_64/fish.app/Contents/Resources/base/usr/local/bin/$(basename $FILE)" rcodesign macho-universal-create --output "$FILE" "$FILE" "$X86_FILE" # macho-universal-create screws up the permissions. chmod 755 "$FILE" done if test -n "$SIGN"; then echo "Signing app" ARGS=( --p12-file "$P12_APP_FILE" --p12-password "$P12_PASSWORD" --code-signature-flags runtime --for-notarization ) if [ -n "$ENTITLEMENTS_FILE" ]; then ARGS+=(--entitlements-xml-file "$ENTITLEMENTS_FILE") fi (set +x; rcodesign sign "${ARGS[@]}" "fish.app") fi cp -R "fish.app" "$OUTPUT_PATH/fish-$VERSION.app" cd "$OUTPUT_PATH" # Maybe notarize. if test -n "$NOTARIZE"; then echo "Notarizing" rcodesign notarize --staple --wait --max-wait-seconds 1800 --api-key-file "$API_KEY_FILE" "$OUTPUT_PATH/fish-$VERSION.pkg" rcodesign notarize --staple --wait --max-wait-seconds 1800 --api-key-file "$API_KEY_FILE" "$OUTPUT_PATH/fish-$VERSION.app" fi # Zip it up. zip -r "fish-$VERSION.app.zip" "fish-$VERSION.app" && rm -Rf "fish-$VERSION.app" rm -rf "$PKGDIR"