#!/usr/bin/env sh set -eu VERSION="0.1.2" TARGET="aarch64-apple-darwin" PKG="egret-${VERSION}-${TARGET}" DEFAULT_URL="https://github.com/egret-lang/release/releases/download/pre-release/${PKG}.zip" DEFAULT_INSTALL_DIR="${HOME}/.local/bin" INSTALL_DIR="${EGRET_INSTALL_DIR:-$DEFAULT_INSTALL_DIR}" DOWNLOAD_URL="${EGRET_INSTALL_URL:-$DEFAULT_URL}" ASSUME_YES=0 INSTALL_DEPS=1 CREATE_LINKS=1 init_colors() { color_enabled=0 if [ "${NO_COLOR:-}" != "" ]; then color_enabled=0 elif [ "${EGRET_INSTALL_COLOR:-}" = "always" ]; then color_enabled=1 elif [ -t 1 ] || [ -t 2 ]; then color_enabled=1 fi if [ "$color_enabled" -eq 1 ]; then C_RESET="$(printf '\033[0m')" C_BOLD="$(printf '\033[1m')" C_DIM="$(printf '\033[2m')" C_RED="$(printf '\033[31m')" C_GREEN="$(printf '\033[32m')" C_YELLOW="$(printf '\033[33m')" C_BLUE="$(printf '\033[34m')" C_MAGENTA="$(printf '\033[35m')" C_CYAN="$(printf '\033[36m')" C_WHITE="$(printf '\033[37m')" C_BG_BLUE="$(printf '\033[44m')" else C_RESET="" C_BOLD="" C_DIM="" C_RED="" C_GREEN="" C_YELLOW="" C_BLUE="" C_MAGENTA="" C_CYAN="" C_WHITE="" C_BG_BLUE="" fi } show_logo() { size="$(terminal_size)" cols="${size% *}" rows="${size#* }" full_logo_width=88 full_logo_min_rows=14 logo_margin=4 banner_blank "$cols" if [ "$cols" -ge $((full_logo_width + logo_margin)) ] && [ "$rows" -ge "$full_logo_min_rows" ]; then banner_center_width "$cols" "$C_WHITE" 88 '███████╗ ██████╗ ██████╗ ███████╗████████╗ ██╗ █████╗ ███╗ ██╗ ██████╗ ' banner_center_width "$cols" "$C_WHITE" 88 '██╔════╝██╔════╝ ██╔══██╗██╔════╝╚══██╔══╝ ██║ ██╔══██╗████╗ ██║██╔════╝ ' banner_center_width "$cols" "$C_WHITE" 88 '█████╗ ██║ ███╗██████╔╝█████╗ ██║ █████╗██║ ███████║██╔██╗ ██║██║ ███╗' banner_center_width "$cols" "$C_WHITE" 88 '██╔══╝ ██║ ██║██╔══██╗██╔══╝ ██║ ╚════╝██║ ██╔══██║██║╚██╗██║██║ ██║' banner_center_width "$cols" "$C_WHITE" 88 '███████╗╚██████╔╝██║ ██║███████╗ ██║ ███████╗██║ ██║██║ ╚████║╚██████╔╝' banner_center_width "$cols" "$C_WHITE" 88 '╚══════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝ ' elif [ "$rows" -ge 8 ]; then banner_center_width "$cols" "$C_WHITE" 10 "EGRET-LANG" else banner_center_width "$cols" "$C_WHITE" 5 "EGRET" fi banner_blank "$cols" banner_center "$cols" "$C_WHITE" "EGRET-LANG INSTALLER ${VERSION}" banner_blank "$cols" } is_uint() { case "${1:-}" in ''|*[!0-9]*) return 1 ;; *) return 0 ;; esac } terminal_size() { cols="${COLUMNS:-}" rows="${LINES:-}" if ! is_uint "$cols"; then cols="" fi if ! is_uint "$rows"; then rows="" fi if { [ "$cols" = "" ] || [ "$rows" = "" ]; } && command -v stty >/dev/null 2>&1; then stty_size="$(stty size < /dev/tty 2>/dev/null || stty size 2>/dev/null || printf '')" set -- $stty_size if [ "$#" -ge 2 ]; then if [ "$rows" = "" ] && is_uint "$1"; then rows="$1" fi if [ "$cols" = "" ] && is_uint "$2"; then cols="$2" fi fi fi if [ "$cols" = "" ] && command -v tput >/dev/null 2>&1; then cols="$(tput cols 2>/dev/null || printf '')" if ! is_uint "$cols"; then cols="" fi fi if [ "$rows" = "" ] && command -v tput >/dev/null 2>&1; then rows="$(tput lines 2>/dev/null || printf '')" if ! is_uint "$rows"; then rows="" fi fi if [ "$cols" = "" ]; then cols=80 fi if [ "$rows" = "" ]; then rows=24 fi if [ "$cols" -lt 20 ]; then cols=20 fi if [ "$rows" -lt 6 ]; then rows=6 fi printf '%s %s\n' "$cols" "$rows" } repeat_spaces() { n="$1" while [ "$n" -gt 0 ]; do printf ' ' n=$((n - 1)) done } banner_blank() { cols="$1" if banner_clear_line; then printf '%s\n' "$C_RESET" return 0 fi printf '%s%s' "$C_BG_BLUE" "$C_BOLD" repeat_spaces "$cols" printf '%s\n' "$C_RESET" } banner_clear_line() { if [ "$C_BG_BLUE" = "" ]; then return 1 fi printf '\r%s%s\033[2K' "$C_BG_BLUE" "$C_BOLD" } banner_text() { cols="$1" color="$2" text="$3" if banner_clear_line; then printf '%s%s%s%s\n' "$C_BG_BLUE" "$color" "$text" "$C_RESET" return 0 fi len=${#text} if [ "$len" -ge "$cols" ]; then printf '%s%s%s%s%s\n' "$C_BG_BLUE" "$color" "$C_BOLD" "$text" "$C_RESET" return 0 fi pad=$((cols - len)) printf '%s%s%s%s' "$C_BG_BLUE" "$color" "$C_BOLD" "$text" repeat_spaces "$pad" printf '%s\n' "$C_RESET" } banner_center() { cols="$1" color="$2" text="$3" len=${#text} if banner_clear_line; then if [ "$len" -ge "$cols" ]; then printf '%s%s%s%s\n' "$C_BG_BLUE" "$color" "$text" "$C_RESET" return 0 fi left=$(((cols - len) / 2)) printf '%s%s%s' "$C_BG_BLUE" "$color" "$C_BOLD" repeat_spaces "$left" printf '%s%s\n' "$text" "$C_RESET" return 0 fi if [ "$len" -ge "$cols" ]; then banner_text "$cols" "$color" "$text" return 0 fi left=$(((cols - len) / 2)) right=$((cols - len - left)) printf '%s%s%s' "$C_BG_BLUE" "$color" "$C_BOLD" repeat_spaces "$left" printf '%s' "$text" repeat_spaces "$right" printf '%s\n' "$C_RESET" } banner_center_width() { cols="$1" color="$2" width="$3" text="$4" if banner_clear_line; then if [ "$width" -ge "$cols" ]; then printf '%s%s%s%s\n' "$C_BG_BLUE" "$color" "$text" "$C_RESET" return 0 fi left=$(((cols - width) / 2)) printf '%s%s%s' "$C_BG_BLUE" "$color" "$C_BOLD" repeat_spaces "$left" printf '%s%s\n' "$text" "$C_RESET" return 0 fi if [ "$width" -ge "$cols" ]; then printf '%s%s%s%s%s\n' "$C_BG_BLUE" "$color" "$C_BOLD" "$text" "$C_RESET" return 0 fi left=$(((cols - width) / 2)) right=$((cols - width - left)) printf '%s%s%s' "$C_BG_BLUE" "$color" "$C_BOLD" repeat_spaces "$left" printf '%s' "$text" repeat_spaces "$right" printf '%s\n' "$C_RESET" } paint() { printf '%s%s%s' "$1" "$2" "$C_RESET" } path_text() { paint "$C_GREEN" "$1" } url_text() { paint "$C_BLUE" "$1" } pkg_text() { paint "$C_MAGENTA" "$1" } usage() { cat <")" "$*" } ok() { printf '%s %s\n' "$(paint "$C_GREEN" "✓")" "$*" } die() { printf '%s %s\n' "$(paint "$C_RED" "error:")" "$*" >&2 exit 1 } need_value() { if [ "${2:-}" = "" ]; then die "$1 requires a value" fi } init_colors while [ "$#" -gt 0 ]; do case "$1" in --dir) need_value "$1" "${2:-}" INSTALL_DIR="$2" shift 2 ;; --url) need_value "$1" "${2:-}" DOWNLOAD_URL="$2" shift 2 ;; --yes|-y) ASSUME_YES=1 shift ;; --no-deps) INSTALL_DEPS=0 shift ;; --no-links) CREATE_LINKS=0 shift ;; -h|--help) usage exit 0 ;; *) die "unknown option: $1" ;; esac done expand_path() { case "$1" in "~") printf '%s\n' "$HOME" ;; "~/"*) printf '%s/%s\n' "$HOME" "${1#~/}" ;; *) printf '%s\n' "$1" ;; esac } read_tty_answer() { if answer="$({ IFS= read -r line < /dev/tty && printf '%s' "$line"; } 2>/dev/null)"; then return 0 fi die "interactive input is unavailable. Re-run from a terminal or use: sh install.sh --yes" } prompt() { question="$1" default="$2" if [ "$ASSUME_YES" -eq 1 ]; then printf '%s\n' "$default" return 0 fi printf '%s %s [%s]: ' "$(paint "$C_CYAN" "?")" "$(paint "$C_BOLD" "$question")" "$(paint "$C_YELLOW" "$default")" >&2 read_tty_answer if [ "$answer" = "" ]; then printf '%s\n' "$default" else printf '%s\n' "$answer" fi } prompt_yes_no() { question="$1" default="$2" if [ "$ASSUME_YES" -eq 1 ]; then [ "$default" = "y" ] || [ "$default" = "Y" ] return $? fi printf '%s %s [%s]: ' "$(paint "$C_CYAN" "?")" "$(paint "$C_BOLD" "$question")" "$(paint "$C_YELLOW" "$default")" >&2 read_tty_answer if [ "$answer" = "" ]; then answer="$default" fi case "$answer" in y|Y|yes|YES|Yes) return 0 ;; *) return 1 ;; esac } have_cmd() { command -v "$1" >/dev/null 2>&1 } detect_platform() { os="$(uname -s 2>/dev/null || printf unknown)" arch="$(uname -m 2>/dev/null || printf unknown)" case "$os" in Darwin) ;; *) die "this installer currently supports macOS only; detected OS: $os" ;; esac case "$arch" in arm64|aarch64) ;; *) die "this package is for ${TARGET}; detected CPU: $arch" ;; esac ok "Detected platform: $(paint "$C_BOLD" "macOS") $(paint "$C_YELLOW" "$arch")" } install_xcode_tools() { if [ "$INSTALL_DEPS" -ne 1 ]; then return 1 fi if ! have_cmd xcode-select; then return 1 fi step "Installing Apple Command Line Tools. Follow the macOS prompt if it appears." xcode-select --install >/dev/null 2>&1 || true log "$(paint "$C_YELLOW" "After Command Line Tools finish installing, run this installer again.")" exit 1 } ensure_tool() { tool="$1" hint="$2" if have_cmd "$tool"; then return 0 fi die "missing required tool: ${tool}. ${hint}" } ensure_compiler() { if have_cmd cc || have_cmd gcc || have_cmd clang; then return 0 fi install_xcode_tools die "missing C compiler. Install Apple Command Line Tools with: xcode-select --install" } ensure_dependencies() { downloader="" if have_cmd curl; then downloader="curl" elif have_cmd wget; then downloader="wget" else die "missing downloader: install curl or wget" fi ensure_tool unzip "Install unzip or Apple Command Line Tools." ensure_tool mktemp "Install Apple Command Line Tools." ensure_tool chmod "Install core system utilities." ensure_compiler ok "Dependency check ok: downloader=$(paint "$C_YELLOW" "$downloader"), unzip, compiler" } download_file() { url="$1" out="$2" if have_cmd curl; then curl -fL --retry 3 --connect-timeout 20 -o "$out" "$url" elif have_cmd wget; then wget -O "$out" "$url" else die "missing downloader: curl or wget" fi } abs_dir() { path="$1" parent="$(dirname "$path")" base="$(basename "$path")" mkdir -p "$parent" parent_abs="$(cd "$parent" && pwd -P)" printf '%s/%s\n' "$parent_abs" "$base" } install_package() { install_dir="$(expand_path "$INSTALL_DIR")" install_dir="$(abs_dir "$install_dir")" dest="${install_dir}/${PKG}" tmp="$(mktemp -d "${TMPDIR:-/tmp}/egret-install.XXXXXX")" trap 'rm -rf "$tmp"' EXIT INT HUP TERM zip_path="${tmp}/${PKG}.zip" step "Downloading package" log " $(url_text "$DOWNLOAD_URL")" download_file "$DOWNLOAD_URL" "$zip_path" step "Extracting package" unzip -q "$zip_path" -d "$tmp" [ -d "${tmp}/${PKG}" ] || die "archive does not contain expected directory: ${PKG}" [ -x "${tmp}/${PKG}/bin/egret" ] || die "archive does not contain executable: ${PKG}/bin/egret" mkdir -p "$install_dir" if [ -e "$dest" ]; then if prompt_yes_no "Replace existing ${dest}?" "y"; then rm -rf "$dest" else die "installation cancelled" fi fi mv "${tmp}/${PKG}" "$dest" if [ "$CREATE_LINKS" -eq 1 ]; then for name in egret eg egretc egret4c egretdoc egrettest egretperf; do if [ "$name" = "eg" ]; then target="${dest}/bin/egret" else target="${dest}/bin/${name}" fi if [ -x "$target" ]; then ln -sf "$target" "${install_dir}/${name}" fi done fi ok "Installed $(pkg_text "Egret ${VERSION}")" log " $(path_text "$dest")" if [ "$CREATE_LINKS" -eq 1 ]; then step "Command links" log " $(path_text "${install_dir}/egret")" log " $(path_text "${install_dir}/egretc")" fi case ":${PATH}:" in *":${install_dir}:"*) ;; *) log "" log "$(paint "$C_YELLOW" "Add this directory to PATH if needed:")" log " export PATH=\"$(path_text "$install_dir"):\$PATH\"" ;; esac if "${install_dir}/egret" version >/dev/null 2>&1; then log "" step "Verification" "${install_dir}/egret" version || true else log "" log "$(paint "$C_YELLOW" "Verification: installed, but '${install_dir}/egret version' did not run successfully.")" fi } show_logo detect_platform ensure_dependencies INSTALL_DIR="$(prompt "Installation directory" "$INSTALL_DIR")" INSTALL_DIR="$(expand_path "$INSTALL_DIR")" if [ "$ASSUME_YES" -ne 1 ]; then log "" step "Install configuration" log " package: $(pkg_text "$PKG")" log " url: $(url_text "$DOWNLOAD_URL")" log " install_dir: $(path_text "$INSTALL_DIR")" if [ "$CREATE_LINKS" -eq 1 ]; then log " command_links: $(paint "$C_GREEN" "yes")" else log " command_links: $(paint "$C_YELLOW" "no")" fi if ! prompt_yes_no "Continue installation?" "y"; then die "installation cancelled" fi fi install_package