installer script for mac
This commit is contained in:
296
build-production-mac.sh
Normal file
296
build-production-mac.sh
Normal file
@ -0,0 +1,296 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# macOS Production Build Script for the SDL3 Tetris project
|
||||
# Mirrors the Windows PowerShell workflow but uses common POSIX tooling so it
|
||||
# can be executed on macOS runners or local developer machines.
|
||||
|
||||
PROJECT_NAME="tetris"
|
||||
BUILD_DIR="build-release"
|
||||
OUTPUT_DIR="dist"
|
||||
PACKAGE_DIR=""
|
||||
VERSION="$(date +"%Y.%m.%d")"
|
||||
CLEAN=0
|
||||
PACKAGE_ONLY=0
|
||||
|
||||
print_usage() {
|
||||
cat <<'USAGE'
|
||||
Usage: ./build-production-mac.sh [options]
|
||||
|
||||
Options:
|
||||
-c, --clean Remove existing build + dist folders before running
|
||||
-p, --package-only Skip compilation and only rebuild the distributable
|
||||
-o, --output DIR Customize output directory (default: dist)
|
||||
-h, --help Show this help text
|
||||
USAGE
|
||||
}
|
||||
|
||||
log() {
|
||||
local level=$1; shift
|
||||
case "$level" in
|
||||
INFO) printf '\033[36m[INFO]\033[0m %s\n' "$*" ;;
|
||||
OK) printf '\033[32m[ OK ]\033[0m %s\n' "$*" ;;
|
||||
WARN) printf '\033[33m[WARN]\033[0m %s\n' "$*" ;;
|
||||
ERR) printf '\033[31m[ERR ]\033[0m %s\n' "$*" ;;
|
||||
*) printf '%s\n' "$*" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
require_macos() {
|
||||
if [[ $(uname) != "Darwin" ]]; then
|
||||
log ERR "This script is intended for macOS hosts."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
parse_args() {
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-c|--clean) CLEAN=1; shift ;;
|
||||
-p|--package-only) PACKAGE_ONLY=1; shift ;;
|
||||
-o|--output)
|
||||
if [[ $# -lt 2 ]]; then
|
||||
log ERR "--output requires a directory argument"
|
||||
exit 1
|
||||
fi
|
||||
OUTPUT_DIR="$2"; shift 2 ;;
|
||||
-h|--help) print_usage; exit 0 ;;
|
||||
*)
|
||||
log ERR "Unknown argument: $1"
|
||||
print_usage
|
||||
exit 1 ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
configure_paths() {
|
||||
PACKAGE_DIR="${OUTPUT_DIR}/TetrisGame-mac"
|
||||
}
|
||||
|
||||
clean_previous() {
|
||||
if (( CLEAN )); then
|
||||
log INFO "Cleaning previous build artifacts..."
|
||||
rm -rf "$BUILD_DIR" "$OUTPUT_DIR"
|
||||
log OK "Previous artifacts removed"
|
||||
fi
|
||||
}
|
||||
|
||||
configure_and_build() {
|
||||
if (( PACKAGE_ONLY )); then
|
||||
return
|
||||
fi
|
||||
|
||||
log INFO "Configuring CMake (Release)..."
|
||||
cmake -S . -B "$BUILD_DIR" -DCMAKE_BUILD_TYPE=Release
|
||||
log OK "CMake configure complete"
|
||||
|
||||
log INFO "Building Release target..."
|
||||
cmake --build "$BUILD_DIR" --config Release
|
||||
log OK "Build finished"
|
||||
}
|
||||
|
||||
resolve_executable() {
|
||||
local candidates=(
|
||||
"$BUILD_DIR/Release/${PROJECT_NAME}"
|
||||
"$BUILD_DIR/${PROJECT_NAME}"
|
||||
"$BUILD_DIR/${PROJECT_NAME}.app/Contents/MacOS/${PROJECT_NAME}"
|
||||
)
|
||||
|
||||
for path in "${candidates[@]}"; do
|
||||
if [[ -x "$path" ]]; then
|
||||
EXECUTABLE_PATH="$path"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ -z ${EXECUTABLE_PATH:-} ]]; then
|
||||
log ERR "Unable to locate built executable."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$EXECUTABLE_PATH" == *.app/Contents/MacOS/* ]]; then
|
||||
APP_BUNDLE_PATH="${EXECUTABLE_PATH%/Contents/MacOS/*}"
|
||||
else
|
||||
APP_BUNDLE_PATH=""
|
||||
fi
|
||||
|
||||
log OK "Using executable: $EXECUTABLE_PATH"
|
||||
}
|
||||
|
||||
prepare_package_dir() {
|
||||
log INFO "Creating package directory $PACKAGE_DIR ..."
|
||||
rm -rf "$PACKAGE_DIR"
|
||||
mkdir -p "$PACKAGE_DIR"
|
||||
}
|
||||
|
||||
copy_binary_or_bundle() {
|
||||
if [[ -n ${APP_BUNDLE_PATH:-} ]]; then
|
||||
log INFO "Copying app bundle..."
|
||||
rsync -a "$APP_BUNDLE_PATH" "$PACKAGE_DIR/"
|
||||
PACKAGE_BINARY="${APP_BUNDLE_PATH##*/}/Contents/MacOS/${PROJECT_NAME}"
|
||||
else
|
||||
log INFO "Copying executable..."
|
||||
cp "$EXECUTABLE_PATH" "$PACKAGE_DIR/${PROJECT_NAME}"
|
||||
chmod +x "$PACKAGE_DIR/${PROJECT_NAME}"
|
||||
PACKAGE_BINARY="${PROJECT_NAME}"
|
||||
fi
|
||||
log OK "Binary ready (${PACKAGE_BINARY})"
|
||||
}
|
||||
|
||||
copy_assets() {
|
||||
log INFO "Copying assets..."
|
||||
local folders=("assets" "fonts")
|
||||
for folder in "${folders[@]}"; do
|
||||
if [[ -d "$folder" ]]; then
|
||||
rsync -a "$folder" "$PACKAGE_DIR/"
|
||||
log OK "Copied $folder"
|
||||
fi
|
||||
done
|
||||
if [[ -f "FreeSans.ttf" ]]; then
|
||||
cp "FreeSans.ttf" "$PACKAGE_DIR/"
|
||||
log OK "Copied FreeSans.ttf"
|
||||
fi
|
||||
}
|
||||
|
||||
copy_dependencies() {
|
||||
log INFO "Collecting dynamic libraries from vcpkg..."
|
||||
local triplets=("arm64-osx" "x64-osx" "universal-osx")
|
||||
local bases=("$BUILD_DIR/vcpkg_installed" "vcpkg_installed")
|
||||
declare -A copied=()
|
||||
|
||||
for base in "${bases[@]}"; do
|
||||
for triplet in "${triplets[@]}"; do
|
||||
for sub in lib bin; do
|
||||
local dir="$base/$triplet/$sub"
|
||||
if [[ -d "$dir" ]]; then
|
||||
while IFS= read -r -d '' dylib; do
|
||||
local name=$(basename "$dylib")
|
||||
if [[ -z ${copied[$name]:-} ]]; then
|
||||
cp "$dylib" "$PACKAGE_DIR/"
|
||||
copied[$name]=1
|
||||
log OK "Copied $name"
|
||||
fi
|
||||
done < <(find "$dir" -maxdepth 1 -type f -name '*.dylib' -print0)
|
||||
fi
|
||||
done
|
||||
done
|
||||
done
|
||||
|
||||
if [[ ${#copied[@]} -eq 0 ]]; then
|
||||
log WARN "No .dylib files found; ensure vcpkg installed macOS triplet dependencies."
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_rpath() {
|
||||
local binary="$PACKAGE_DIR/$PACKAGE_BINARY"
|
||||
if [[ ! -f "$binary" ]]; then
|
||||
return
|
||||
fi
|
||||
if command -v otool >/dev/null 2>&1 && command -v install_name_tool >/dev/null 2>&1; then
|
||||
if ! otool -l "$binary" | grep -A2 LC_RPATH | grep -q '@executable_path'; then
|
||||
log INFO "Adding @executable_path rpath"
|
||||
install_name_tool -add_rpath "@executable_path" "$binary"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
create_launchers() {
|
||||
local launch_command="./${PROJECT_NAME}"
|
||||
if [[ "$PACKAGE_BINARY" == *.app/Contents/MacOS/* ]]; then
|
||||
local app_dir="${PACKAGE_BINARY%%/Contents/*}"
|
||||
launch_command="open \"./${app_dir}\""
|
||||
fi
|
||||
|
||||
{
|
||||
cat <<'CMD'
|
||||
#!/usr/bin/env bash
|
||||
cd "$(dirname "$0")"
|
||||
CMD
|
||||
printf '%s\n' "${launch_command}"
|
||||
} > "$PACKAGE_DIR/Launch-Tetris.command"
|
||||
chmod +x "$PACKAGE_DIR/Launch-Tetris.command"
|
||||
|
||||
cat > "$PACKAGE_DIR/README-mac.txt" <<EOF
|
||||
Tetris SDL3 Game - macOS Release $VERSION
|
||||
=========================================
|
||||
|
||||
Requirements:
|
||||
- macOS 12 Monterey or newer (Apple Silicon or Intel)
|
||||
- GPU with Metal support
|
||||
|
||||
Installation:
|
||||
1. Unzip the archive anywhere (e.g., ~/Games/Tetris).
|
||||
2. Ensure the executable bit stays set (script already does this).
|
||||
3. Double-click Launch-Tetris.command or run the binary/app manually from Terminal or Finder.
|
||||
|
||||
Files:
|
||||
- tetris / Launch-Tetris.command (start the game)
|
||||
- assets/ (art, audio, fonts)
|
||||
- *.dylib from SDL3 and related dependencies
|
||||
- FreeSans.ttf font
|
||||
|
||||
Enjoy!
|
||||
EOF
|
||||
log OK "Created README and launcher"
|
||||
}
|
||||
|
||||
validate_package() {
|
||||
log INFO "Validating package contents..."
|
||||
local missing=()
|
||||
for required in "$PACKAGE_BINARY" assets FreeSans.ttf; do
|
||||
if [[ ! -e "$PACKAGE_DIR/$required" ]]; then
|
||||
missing+=("$required")
|
||||
fi
|
||||
done
|
||||
if (( ${#missing[@]} > 0 )); then
|
||||
log WARN "Missing: ${missing[*]}"
|
||||
else
|
||||
log OK "Package looks complete"
|
||||
fi
|
||||
}
|
||||
|
||||
create_zip() {
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
local zip_name="TetrisGame-mac-${VERSION}.zip"
|
||||
local zip_path="$OUTPUT_DIR/$zip_name"
|
||||
log INFO "Creating zip archive $zip_path ..."
|
||||
if command -v ditto >/dev/null 2>&1; then
|
||||
ditto -c -k --keepParent "$PACKAGE_DIR" "$zip_path"
|
||||
else
|
||||
(cd "$OUTPUT_DIR" && zip -r "$zip_name" "$(basename "$PACKAGE_DIR")")
|
||||
fi
|
||||
log OK "Zip created"
|
||||
}
|
||||
|
||||
main() {
|
||||
parse_args "$@"
|
||||
require_macos
|
||||
configure_paths
|
||||
|
||||
if [[ ! -f "CMakeLists.txt" ]]; then
|
||||
log ERR "Run from repository root (CMakeLists.txt missing)."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log INFO "======================================"
|
||||
log INFO " macOS Production Builder"
|
||||
log INFO "======================================"
|
||||
log INFO "Version: $VERSION"
|
||||
log INFO "Output: $OUTPUT_DIR"
|
||||
|
||||
clean_previous
|
||||
configure_and_build
|
||||
resolve_executable
|
||||
prepare_package_dir
|
||||
copy_binary_or_bundle
|
||||
copy_assets
|
||||
copy_dependencies
|
||||
ensure_rpath
|
||||
create_launchers
|
||||
validate_package
|
||||
create_zip
|
||||
|
||||
log INFO "Done. Package available at $PACKAGE_DIR"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user