#!/usr/bin/env bash
#
# Sunfish — autocompilación de sunfish-cli (NativeAOT) para Linux y macOS.
# (Sunfish.exe, la ventana, es solo para Windows: ver build.ps1.)
#
# curl -fsSL https://raw.githubusercontent.com/XWolfOverride/Sunfish/main/build.sh | bash
#
# Qué hace:
# 1. Comprueba que están instaladas las herramientas necesarias (NO instala nada).
# 2. Descarga (o actualiza) la última versión del código con git.
# 3. Compila `sunfish-cli` con NativeAOT.
# 4. Deja el binario en la raíz del proyecto y te explica cómo instalarlo.
#
# Variables opcionales:
# SUNFISH_REPO_URL URL del repositorio (útil para forks)
set -euo pipefail
REPO_URL="${SUNFISH_REPO_URL:-https://github.com/XWolfOverride/Sunfish}"
DIR_NAME="Sunfish"
if [ -t 1 ]; then
B=$'\e[1m'; G=$'\e[32m'; R=$'\e[31m'; Y=$'\e[33m'; C=$'\e[36m'; N=$'\e[0m'
else
B=''; G=''; R=''; Y=''; C=''; N=''
fi
ok() { printf '%s✔%s %s\n' "$G" "$N" "$*"; }
bad() { printf '%s✖%s %s\n' "$R" "$N" "$*"; }
info() { printf '%s»%s %s\n' "$C" "$N" "$*"; }
die() { printf '\n%s%s%s\n' "$R" "$*" "$N" >&2; exit 1; }
echo
echo "${B}Sunfish — autocompilación${N}"
echo
# ---------------------------------------------------------------- sistema
OS="$(uname -s)"
case "$OS" in
Linux) PREFIX="linux" ;;
Darwin) PREFIX="osx" ;;
*) die "Sistema no soportado por este script: $OS. En Windows usa build.ps1." ;;
esac
case "$(uname -m)" in
x86_64|amd64) RID="$PREFIX-x64" ;;
aarch64|arm64) RID="$PREFIX-arm64" ;;
*) die "Arquitectura no soportada por el script: $(uname -m)" ;;
esac
info "Sistema detectado: $RID"
echo
# ---------------------------------------------------------------- gestor de paquetes (solo para sugerir)
PM=""
if [ "$OS" = "Darwin" ]; then
PM="brew"
elif command -v apt-get >/dev/null 2>&1; then
PM="apt"
elif command -v dnf >/dev/null 2>&1; then
PM="dnf"
elif command -v pacman >/dev/null 2>&1; then
PM="pacman"
fi
# ---------------------------------------------------------------- prerrequisitos
missing=() # descripción de lo que falta
pkgs=() # paquetes del gestor del sistema que lo resolverían
manual=() # lo que no se resuelve con el gestor
if command -v git >/dev/null 2>&1; then
ok "git ($(git --version | awk '{print $3}'))"
else
bad "git"; missing+=("git"); pkgs+=("git")
fi
dotnet_ok=0
if command -v dotnet >/dev/null 2>&1; then
dotnet_ver="$(dotnet --version 2>/dev/null || true)"
dotnet_major="${dotnet_ver%%.*}"
if [[ "$dotnet_major" =~ ^[0-9]+$ ]] && [ "$dotnet_major" -ge 10 ]; then
ok "SDK de .NET ($dotnet_ver)"
dotnet_ok=1
else
bad "SDK de .NET: versión ${dotnet_ver:-desconocida}, se necesita 10 o superior"
fi
else
bad "SDK de .NET 10"
fi
if [ "$dotnet_ok" -eq 0 ]; then
missing+=("SDK de .NET 10")
if [ "$OS" = "Darwin" ]; then
manual+=("brew install --cask dotnet-sdk")
else
manual+=("SDK de .NET 10: https://learn.microsoft.com/dotnet/core/install/linux (o: curl -sSL https://dot.net/v1/dotnet-install.sh | bash -s -- --channel 10.0)")
fi
fi
if [ "$OS" = "Darwin" ]; then
# En macOS el compilador y el enlazador vienen con las Command Line Tools de Xcode.
if xcode-select -p >/dev/null 2>&1 && command -v clang >/dev/null 2>&1; then
ok "Xcode Command Line Tools"
else
bad "Xcode Command Line Tools"; missing+=("Xcode Command Line Tools"); manual+=("xcode-select --install")
fi
else
if command -v clang >/dev/null 2>&1; then
ok "clang ($(clang --version | head -n1 | sed -E 's/.*version ([0-9.]+).*/\1/'))"
else
bad "clang"; missing+=("clang"); pkgs+=("clang")
fi
if command -v ld >/dev/null 2>&1; then
ok "enlazador (ld)"
else
bad "enlazador (ld)"; missing+=("binutils")
case "$PM" in
apt) pkgs+=("build-essential") ;;
dnf) pkgs+=("binutils") ;;
pacman) pkgs+=("base-devel") ;;
*) pkgs+=("binutils") ;;
esac
fi
have_zlib=0
if [ -f /usr/include/zlib.h ]; then
have_zlib=1
elif command -v clang >/dev/null 2>&1 \
&& printf '#include <zlib.h>\nint main(void){return 0;}\n' | clang -x c -fsyntax-only - >/dev/null 2>&1; then
have_zlib=1
fi
if [ "$have_zlib" -eq 1 ]; then
ok "cabeceras de zlib"
else
bad "cabeceras de zlib"; missing+=("zlib (cabeceras)")
case "$PM" in
apt) pkgs+=("zlib1g-dev") ;;
dnf) pkgs+=("zlib-devel") ;;
pacman) pkgs+=("zlib") ;;
*) pkgs+=("zlib (cabeceras de desarrollo)") ;;
esac
fi
fi
if [ "${#missing[@]}" -gt 0 ]; then
echo
printf '%sFaltan herramientas necesarias:%s %s\n' "$Y" "$N" "$(IFS=,; echo "${missing[*]}" | sed 's/,/, /g')"
echo
if [ "${#pkgs[@]}" -gt 0 ]; then
echo "Instálalas con (este script no instala nada por ti):"
echo
case "$PM" in
apt) echo " sudo apt update && sudo apt install -y ${pkgs[*]}" ;;
dnf) echo " sudo dnf install -y ${pkgs[*]}" ;;
pacman) echo " sudo pacman -S --needed ${pkgs[*]}" ;;
brew) echo " brew install ${pkgs[*]}" ;;
*) echo " (con el gestor de paquetes de tu sistema) ${pkgs[*]}" ;;
esac
echo
fi
for m in "${manual[@]}"; do
echo " · $m"
done
echo
echo "Después vuelve a ejecutar este script. Detalles en el README del proyecto."
exit 1
fi
# ---------------------------------------------------------------- código fuente
echo
update() {
# Sin remoto (copia local aún sin publicar) no hay nada que actualizar.
if [ -n "$(git -C "$1" remote)" ]; then
git -C "$1" pull --ff-only || die "No se pudo actualizar (¿hay cambios locales?). Resuélvelo y repite."
else
info "El repositorio no tiene remoto configurado: se compila el código tal como está."
fi
}
if [ -f Sunfish.slnx ] && [ -d .git ]; then
ROOT="$PWD"
info "Usando el repositorio actual: $ROOT"
update "$ROOT"
elif [ -d "$DIR_NAME/.git" ]; then
ROOT="$PWD/$DIR_NAME"
info "Actualizando $ROOT"
update "$ROOT"
else
ROOT="$PWD/$DIR_NAME"
info "Descargando $REPO_URL"
git clone --depth 1 "$REPO_URL" "$ROOT" || die "No se pudo clonar el repositorio."
fi
# ---------------------------------------------------------------- compilación
export DOTNET_NOLOGO=1
export DOTNET_CLI_UI_LANGUAGE=en
rm -rf "$ROOT/publish"
info "Compilando sunfish-cli (NativeAOT, $RID)... puede tardar un minuto"
dotnet publish "$ROOT/src/Sunfish.Cli/Sunfish.Cli.csproj" -c Release -r "$RID" \
-o "$ROOT/publish/cli" -nologo -v q \
|| die "Falló la compilación de sunfish-cli."
[ -f "$ROOT/publish/cli/sunfish-cli" ] || die "No se encontró el binario sunfish-cli tras compilar."
cp -f "$ROOT/publish/cli/sunfish-cli" "$ROOT/sunfish-cli"
chmod +x "$ROOT/sunfish-cli"
rm -rf "$ROOT/publish"
ok "sunfish-cli → $ROOT/sunfish-cli"
# ---------------------------------------------------------------- final
echo
echo "${B}${G}Listo.${N} El binario está en: ${B}$ROOT/sunfish-cli${N}"
ls -lh "$ROOT/sunfish-cli" | awk '{printf " %s %s\n", $5, $9}'
echo
echo "${B}Para instalarlo${N} elige una opción (desde ${C}$ROOT${N}):"
echo
echo " ${B}Solo para tu usuario${N} (sin sudo; asegúrate de que ~/.local/bin está en tu PATH):"
echo " mkdir -p ~/.local/bin && install -m 755 sunfish-cli ~/.local/bin/"
echo
echo " ${B}Para todos los usuarios${N}:"
echo " sudo install -m 755 sunfish-cli /usr/local/bin/"
echo
echo "Pruébalo con: sunfish-cli --help (o sunfish-cli CARPETA para compartir una carpeta)"
echo "Los puertos por debajo de 1024 necesitan privilegios en Linux y macOS: el de por defecto es 8080."
echo "Para actualizar a una nueva versión, vuelve a ejecutar este script."
echo