#!/bin/sh
# Scaly installer — https://scaly.io
#
#   curl -fsSL https://scaly.io/install.sh | sh
#
# Installs Scaly into ~/.scaly with NO sudo — three programs:
#   scaly     the tool: `scaly run`, `scaly build`, `scaly test`; alone, the REPL
#   scalyc    the compiler, with a C compiler's command line (-o -c -S -O2)
#   scalyls   the language server the VS Code extension starts
# and beside them the standard library and the standard packages (http, https,
# tls, json, compress, pg, redis, h3, sqlite, tds) as sources.
#
# The programs are downloaded ready to run for
#   macOS 26 (Apple silicon), macOS 15 and newer (Intel),
#   Ubuntu 26.04 and newer (arm64, x86-64)
# and need nothing but a C compiler, which links the programs you build.
# Where there is none this script FETCHES it (2026-10-04) -- Apple's Command
# Line Tools on macOS, clang through apt on Ubuntu -- and all that asks of you
# is your password, once; SCALY_BUILD_TOOLS=skip leaves it out
# (by hand: xcode-select --install; sudo apt install clang).
#
# On any other macOS or Linux, Scaly is BUILT here from its seed — the three
# programs as LLVM IR — for which LLVM 21 must be present:
#   macOS    brew install llvm@21
#   Ubuntu   sudo apt install llvm-21 llvm-21-dev lld-21 clang
#
# Before anything is fetched, the script shows Scaly's license (MIT) and asks
# whether you accept it; nothing is installed without. Where there is no
# terminal to ask on, SCALY_ACCEPT_LICENSE=1 is the acceptance.
#
# Environment overrides:
#   SCALY_ACCEPT_LICENSE  set to 1 to accept the license without being asked
#   SCALY_PREFIX          install location           (default ~/.scaly)
#   SCALY_VERSION         version to fetch           (default: the newest,
#                         which <base>/downloads/latest names)
#   SCALY_INSTALL_BASE    base URL for downloads     (default https://scaly.io)
#   SCALY_NO_MODIFY_PATH  set to 1 to skip editing your shell profile
#   SCALY_FROM_SEED       set to 1 to build from the seed in any case
#   SCALY_BUILD_TOOLS     install (default) | skip -- what to do where no C
#                         compiler is found
#   LLVM21                path to an LLVM 21 install (building from the seed)
#   CC                    the C compiler to use      (default clang)
#
# Layout:  ~/.scaly/bin        the three commands (put on PATH): links to
#                              the programs, which find their packages
#                              beside themselves -- no script, no variable
#          ~/.scaly/toolchain  the programs, the packages, the seed
#          ~/.scaly/cache      compiled packages (made on first use)
# Running the script again replaces the toolchain; a run that fails leaves the
# installed one as it was.
#
# Uninstall: rm -rf ~/.scaly  and remove the PATH line this script adds.
#
# ★The seed route repeats the recipe of tools/build-from-seed.sh in the source
# tree and must follow it; tests/install/run.sh installs by both routes from
# archives made of the tree and runs what the installed programs must be able
# to do.
set -eu

VERSION="${SCALY_VERSION:-}"
PREFIX="${SCALY_PREFIX:-$HOME/.scaly}"
BASE_URL="${SCALY_INSTALL_BASE:-https://scaly.io}"
LLVM_MAJOR=21

say()  { printf 'scaly-install: %s\n' "$1"; }
die()  { printf 'scaly-install: error: %s\n' "$1" >&2; exit 1; }
have() { command -v "$1" >/dev/null 2>&1; }

OS="$(uname -s)"
case "$OS" in
  Darwin|Linux) ;;
  *) die "this installer covers macOS and Linux; $OS is not one of them" ;;
esac

SYSTEM="$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m)"

have curl || have wget || die "need curl or wget to download the distribution"
have tar || die "need tar to unpack the distribution"

# ---------------------------------------------------------------------------
# 0. The license, before anything is fetched or changed: it is shown, and
#    nothing is installed unless it is accepted -- at the terminal, or, where
#    there is none to ask on (a Dockerfile, CI), by SCALY_ACCEPT_LICENSE=1.
#    The text below is LICENSE's, word for word (tools/publish-install.sh
#    checks it).
# ---------------------------------------------------------------------------
license_text() {
  cat <<'LICENSE_TEXT'
The MIT License (MIT)

Copyright (c) 2016-2026 Ralf Schleitzer

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
LICENSE_TEXT
}
license_notice() {
  printf '\nScaly is distributed under this license:\n\n'
  license_text | sed 's/^/    /'
  printf '\nThe programs also contain third-party software under its own licenses:\n'
  printf '%s\n' "$BASE_URL/downloads/THIRD-PARTY-LICENSES.txt"
  printf 'Both files are installed with Scaly (LICENSE, THIRD-PARTY-LICENSES.txt).\n\n'
}
if [ "${SCALY_ACCEPT_LICENSE:-}" = 1 ]; then
  license_notice
  say "the license is accepted (SCALY_ACCEPT_LICENSE=1)"
elif ( exec < /dev/tty ) 2> /dev/null; then
  # piped into sh, standard input is this script: the answer comes from the
  # terminal, and so that it is seen, the question goes there too
  license_notice > /dev/tty
  printf 'Do you accept the license? [y/N] ' > /dev/tty
  ANSWER=""
  read -r ANSWER < /dev/tty || ANSWER=""
  case "$ANSWER" in
    y|Y|yes|Yes|YES) say "the license is accepted" ;;
    *) printf 'scaly-install: the license was not accepted; nothing was installed.\n' >&2; exit 1 ;;
  esac
else
  license_notice
  die "the license must be accepted before Scaly is installed, and there is no terminal to ask on. To accept it, set SCALY_ACCEPT_LICENSE=1:  curl -fsSL $BASE_URL/install.sh | SCALY_ACCEPT_LICENSE=1 sh"
fi

# fetch <url> <file>: quiet, and a failure is the caller's to explain
fetch() {
  if have curl; then curl -fsSL "$1" -o "$2" 2>/dev/null
  else wget -q -O "$2" "$1" 2>/dev/null; fi
}

# The version: the one asked for, else the newest published -- a version's
# archives do not change once they are out, so the default must move.
if [ -z "$VERSION" ]; then
  LATEST="$(mktemp)"
  fetch "$BASE_URL/downloads/latest" "$LATEST" || die "cannot read $BASE_URL/downloads/latest - set SCALY_VERSION to the version wanted"
  VERSION="$(tr -d ' \r\n' < "$LATEST")"; rm -f "$LATEST"
  [ -n "$VERSION" ] || die "$BASE_URL/downloads/latest names no version"
fi
TARBALL="scaly-$VERSION.tar.gz"

# ---------------------------------------------------------------------------
# 1. A C compiler: it links the programs you build and compiles a package's C
#    files; the seed route also builds Scaly itself with it. clang first — the
#    installed programs call `clang` themselves when nothing else is named.
# ---------------------------------------------------------------------------
CC="${CC:-}"
if [ -z "$CC" ]; then
  for c in clang "clang-$LLVM_MAJOR" cc gcc; do
    if have "$c"; then CC="$c"; break; fi
  done
fi
if [ -n "$CC" ] && ! have "$CC"; then die "the C compiler $CC was not found"; fi
# A macOS without the Command Line Tools has /usr/bin/clang, cc and gcc all the
# same: placeholders that ask for the tools' installation and compile nothing.
# xcode-select -p tells the two apart without raising that dialog.
if [ "$OS" = "Darwin" ] && [ -n "$CC" ] && ! xcode-select -p >/dev/null 2>&1; then
  case "$(command -v "$CC")" in /usr/bin/*) CC="" ;; esac
fi

prerequisites() {
  cat >&2 <<EOF
scaly-install: error: $1
  $SEED_REASON, so Scaly is
  built here from its seed, with LLVM $LLVM_MAJOR:
    macOS:   brew install llvm@$LLVM_MAJOR
    Ubuntu:  sudo apt install llvm-$LLVM_MAJOR llvm-$LLVM_MAJOR-dev lld-$LLVM_MAJOR clang
  Then run this again, or set LLVM$LLVM_MAJOR=/path/to/llvm if it lives elsewhere.
EOF
  exit 1
}

# llvm_tool <name>: the prefix's own, else the versioned name on PATH
llvm_tool() {
  for cand in "$LLVM_PREFIX/bin/$1" "$LLVM_PREFIX/bin/$1-$LLVM_MAJOR" "$1-$LLVM_MAJOR"; do
    if have "$cand"; then printf '%s\n' "$cand"; return 0; fi
  done
  return 1
}

# What the seed route needs: LLVM 21 (llc, opt, llvm-link, libLLVM), ar, and
# on Linux lld.
find_llvm() {
  LLVM_PREFIX=""
  if [ -n "${LLVM21:-}" ]; then
    LLVM_PREFIX="$LLVM21"
  elif have brew && brew --prefix "llvm@$LLVM_MAJOR" >/dev/null 2>&1; then
    LLVM_PREFIX="$(brew --prefix "llvm@$LLVM_MAJOR")"
  elif [ -d "/usr/lib/llvm-$LLVM_MAJOR" ]; then
    LLVM_PREFIX="/usr/lib/llvm-$LLVM_MAJOR"
  elif have "llvm-config-$LLVM_MAJOR"; then
    LLVM_PREFIX="$("llvm-config-$LLVM_MAJOR" --prefix)"
  fi
  [ -n "$LLVM_PREFIX" ] && [ -d "$LLVM_PREFIX" ] || prerequisites "LLVM $LLVM_MAJOR not found."

  # llc, opt and llvm-link come in one package on both systems (Ubuntu's
  # llvm-21, NOT llvm-21-dev; a libLLVM another package pulled in is not the tools).
  LLC="$(llvm_tool llc)"             || prerequisites "llc of LLVM $LLVM_MAJOR not found (Ubuntu: the package llvm-$LLVM_MAJOR)."
  OPT="$(llvm_tool opt)"             || prerequisites "opt of LLVM $LLVM_MAJOR not found (Ubuntu: the package llvm-$LLVM_MAJOR)."
  LLVM_LINK="$(llvm_tool llvm-link)" || prerequisites "llvm-link of LLVM $LLVM_MAJOR not found (Ubuntu: the package llvm-$LLVM_MAJOR)."

  LLVM_LIBDIR="${LLVM_LIBDIR:-$LLVM_PREFIX/lib}"
  LLVM_LIBNAME="${LLVM_LIBNAME:-LLVM-$LLVM_MAJOR}"
  ls "$LLVM_LIBDIR"/lib"$LLVM_LIBNAME".* >/dev/null 2>&1 \
    || prerequisites "lib$LLVM_LIBNAME not found in $LLVM_LIBDIR (Ubuntu: the package llvm-$LLVM_MAJOR-dev)."

  if [ -z "$CC" ] && have "$LLVM_PREFIX/bin/clang"; then CC="$LLVM_PREFIX/bin/clang"; fi
  [ -n "$CC" ] || prerequisites "no C compiler found (clang, clang-$LLVM_MAJOR, cc or gcc)."

  AR=""
  for c in ar "$LLVM_PREFIX/bin/llvm-ar" "llvm-ar-$LLVM_MAJOR"; do
    if have "$c"; then AR="$c"; break; fi
  done
  [ -n "$AR" ] || die "no ar found (ar or llvm-ar)"

  # On Linux the GNU linker fails to link libLLVM ("failed to set dynamic
  # section sizes"); lld links it. And an ELF executable exports a symbol to
  # its own in-process JIT only when asked to (-rdynamic) — `scaly run`,
  # `scaly test` and the REPL find the runtime in the program itself.
  LINK_FLAGS=""
  if [ "$OS" = "Linux" ]; then
    LLD=""
    for c in "$LLVM_PREFIX/bin/ld.lld" "ld.lld-$LLVM_MAJOR" ld.lld; do
      if have "$c"; then LLD="$(command -v "$c")"; break; fi
    done
    [ -n "$LLD" ] || prerequisites "lld not found (Ubuntu: the package lld-$LLVM_MAJOR)."
    LINK_FLAGS="-fuse-ld=$LLD -rdynamic"
  fi
  say "LLVM $LLVM_MAJOR at $LLVM_PREFIX, C compiler $CC"
}

# ---------------------------------------------------------------------------
# 2. Download and unpack into a NEW toolchain directory beside the installed
#    one; it takes its place only when everything below succeeded. First what
#    is the same for every system: the packages and the seed.
# ---------------------------------------------------------------------------
WORK="$(mktemp -d)"
NEW="$PREFIX/toolchain.new"
TOOLCHAIN="$PREFIX/toolchain"
trap 'rm -rf "$WORK" "$NEW"' EXIT

# shasum is a Perl script and a fresh Ubuntu has only coreutils' sha256sum.
if have sha256sum; then SHA256="sha256sum"
elif have shasum; then SHA256="shasum -a 256"
else SHA256=""; fi
# The checksums of the version's archives, published beside them: each one is
# checked against its line before it is unpacked. A version published
# without the file, or a machine with neither tool, installs unchecked and
# says so.
SUMS="$WORK/SHA256SUMS"
if ! fetch "$BASE_URL/downloads/SHA256SUMS-$VERSION" "$SUMS"; then
  rm -f "$SUMS"
  say "no checksums published for $VERSION: the archives are not checked"
elif [ -z "$SHA256" ]; then
  say "neither sha256sum nor shasum here: the archives are not checked"
fi
# verify <file>: its SHA-256 against the line of its name in SUMS
verify() {
  [ -f "$SUMS" ] && [ -n "$SHA256" ] || return 0
  want="$(awk -v n="$(basename "$1")" '$2 == n { print $1 }' "$SUMS")"
  [ -n "$want" ] || die "$(basename "$1") is not listed in SHA256SUMS-$VERSION"
  got="$($SHA256 "$1" | awk '{ print $1 }')"
  [ "$got" = "$want" ] || die "checksum mismatch for $(basename "$1") - the download is damaged or not the one published"
}

URL="$BASE_URL/downloads/$TARBALL"
say "downloading $URL"
if ! fetch "$URL" "$WORK/$TARBALL"; then
  [ -n "${SCALY_VERSION:-}" ] && die "Scaly $VERSION is not available at $BASE_URL (versions before 0.2.29 are no longer offered) - leave SCALY_VERSION unset to install the newest"
  die "download failed: $URL"
fi
verify "$WORK/$TARBALL"

rm -rf "$NEW"
mkdir -p "$NEW"
tar -xzf "$WORK/$TARBALL" -C "$NEW" || die "could not unpack $TARBALL"
SEED="$NEW/seed"
[ -f "$SEED/scalyc.ll" ] || die "distribution missing seed/scalyc.ll"

STDLIB=""
for d in "$NEW"/packages/scaly/*/; do
  [ -f "$d/scaly.scaly" ] && STDLIB="${d%/}"
done
[ -n "$STDLIB" ] || die "distribution missing the standard library (packages/scaly)"

mkdir -p "$WORK/check"
printf 'print("Hello, World!")\n' > "$WORK/check/hello.scaly"

# ---------------------------------------------------------------------------
# 3. The programs, ready-made for this system when there are any: an archive
#    of libexec/ and lib/. They are taken only if they START here — an older
#    macOS or C library than they were built for sends us to the seed instead.
# ---------------------------------------------------------------------------
READY=0
# why the seed, for the error that says what it needs
SEED_REASON="SCALY_FROM_SEED=1 asks for it"
if [ "${SCALY_FROM_SEED:-0}" != "1" ]; then
  BIN_TARBALL="scaly-$VERSION-$SYSTEM.tar.gz"
  if fetch "$BASE_URL/downloads/$BIN_TARBALL" "$WORK/$BIN_TARBALL"; then
    verify "$WORK/$BIN_TARBALL"
    if tar -xzf "$WORK/$BIN_TARBALL" -C "$NEW" 2>/dev/null \
       && SCALY_HOME="$NEW" "$NEW/libexec/scalyc" -S -o "$WORK/check/probe.ll" "$WORK/check/hello.scaly" >/dev/null 2>&1; then
      READY=1
      say "installed the programs for $SYSTEM"
    else
      rm -rf "$NEW/libexec" "$NEW/lib"
      SEED_REASON="The ready-made programs for $SYSTEM do not start on this machine"
      say "the ready-made programs do not start on this system; building from the seed instead"
    fi
  else
    SEED_REASON="There are no ready-made programs for this system ($SYSTEM)"
    say "no ready-made programs for $SYSTEM; building from the seed instead"
  fi
fi

if [ "$READY" = 0 ]; then
find_llvm

if [ -n "$SHA256" ] && [ -f "$SEED/SHA256SUMS" ]; then
  ( cd "$SEED" && $SHA256 -c SHA256SUMS >/dev/null 2>&1 ) || die "seed checksum mismatch — the download is damaged"
fi

# ---------------------------------------------------------------------------
# 4. (seed) The runtime's native files: the fiber context switch (assembly, one file
#    per ABI), evented I/O, civil time and the catch point (C). Every program
#    that contains the standard library links them.
# ---------------------------------------------------------------------------
case "$(uname -m)" in
  arm64|aarch64) FCONTEXT="$STDLIB/scaly/fiber/fcontext_arm64.S" ;;
  x86_64|amd64)  FCONTEXT="$STDLIB/scaly/fiber/fcontext_x86_64.S" ;;
  *) die "unsupported architecture $(uname -m) (arm64 and x86_64 are)" ;;
esac
say "compiling the runtime's native files"
"$CC" -c "$FCONTEXT" -o "$WORK/fcontext.o"                         || die "assembling $(basename "$FCONTEXT") failed"
"$CC" -O2 -c "$STDLIB/scaly/fiber/eio.c"   -o "$WORK/eio.o"        || die "compiling eio.c failed"
"$CC" -O2 -c "$STDLIB/scaly/time/ctime.c"  -o "$WORK/ctime.o"      || die "compiling ctime.c failed"
"$CC" -O2 -c "$STDLIB/scaly/memory/panic.c" -o "$WORK/panic.o"     || die "compiling panic.c failed"
# the compiler's one C++ file (WebAssembly's exception model): the C
# compiler's C++ twin, against the headers of the LLVM found above
[ -f "$LLVM_PREFIX/include/llvm/Target/TargetMachine.h" ] || prerequisites "LLVM $LLVM_MAJOR has no C++ headers under $LLVM_PREFIX/include (its development package)."
case "$CC" in
  *clang*) CXX="${CXX:-${CC%clang*}clang++${CC##*clang}}" ;;
  *)       CXX="${CXX:-c++}" ;;
esac
"$CXX" -std=c++17 -O2 -fno-rtti -fno-exceptions -I"$LLVM_PREFIX/include" \
  -c "$NEW/native/wasm_eh.cpp" -o "$WORK/wasm_eh.o"                || die "compiling wasm_eh.cpp failed"

# ---------------------------------------------------------------------------
# 5. (seed) The three programs, each its roots linked into one module and optimised
#    as a whole. scalyc and scaly run programs in process (--jit, `scaly run`,
#    `scaly test`, the REPL), and such a program finds the runtime IN the
#    program that runs it — so for those two every definition is kept
#    (linkonce_odr -> weak_odr, before the modules are linked) and stays a
#    visible symbol (unnamed_addr removed, after the optimiser has used it).
# ---------------------------------------------------------------------------
# build_program <name> <keeps runtime visible: 1|0> <root>...
build_program() {
  name="$1"; visible="$2"; shift 2
  inputs=""
  for f in "$@"; do
    if [ "$visible" = 1 ]; then
      sed 's/^define linkonce_odr /define weak_odr /' "$SEED/$f.ll" > "$WORK/$name.$f.ll"
    else
      cp "$SEED/$f.ll" "$WORK/$name.$f.ll"
    fi
    inputs="$inputs $WORK/$name.$f.ll"
  done
  # shellcheck disable=SC2086
  "$LLVM_LINK" -S $inputs -o "$WORK/$name.linked.ll"
  "$OPT" -O2 -S "$WORK/$name.linked.ll" -o "$WORK/$name.opt.ll"
  if [ "$visible" = 1 ]; then
    sed -e '/^define /s/local_unnamed_addr //g' -e '/^define /s/unnamed_addr //g' \
      "$WORK/$name.opt.ll" > "$WORK/$name.final.ll"
  else
    mv "$WORK/$name.opt.ll" "$WORK/$name.final.ll"
  fi
  # -relocation-model=pic: x86-64 Linux links position-independent executables.
  "$LLC" -relocation-model=pic -filetype=obj "$WORK/$name.final.ll" -o "$WORK/$name.o"
  # -lm last: on Linux the math functions are a library of their own, and the
  # linker takes from a library only what is still undefined when it reads it.
  # shellcheck disable=SC2086
  "$CC" $LINK_FLAGS "$WORK/$name.o" "$WORK/fcontext.o" "$WORK/eio.o" "$WORK/ctime.o" "$WORK/panic.o" "$WORK/wasm_eh.o" \
    -L"$LLVM_LIBDIR" -l"$LLVM_LIBNAME" -lm -o "$NEW/libexec/$name"
}

mkdir -p "$NEW/libexec" "$NEW/lib"
say "building scalyc, scaly and scalyls from the seed (about a minute)"
( build_program scalyc  1 main scalyc scaly )                      > "$WORK/scalyc.log"  2>&1 & pid_c=$!
( build_program scaly   1 scaly_main scalyc scaly )                > "$WORK/scaly.log"   2>&1 & pid_t=$!
( build_program scalyls 0 scalyls_main scalyls json scalyc scaly ) > "$WORK/scalyls.log" 2>&1 & pid_l=$!
failed=""
wait "$pid_c" || failed="$failed scalyc"
wait "$pid_t" || failed="$failed scaly"
wait "$pid_l" || failed="$failed scalyls"
if [ -n "$failed" ]; then
  for name in $failed; do
    printf 'scaly-install: building %s failed:\n' "$name" >&2
    tail -20 "$WORK/$name.log" >&2
  done
  die "could not build:$failed"
fi

# ---------------------------------------------------------------------------
# 6. (seed) The runtime archive `scalyc -o` links a program against (`scaly build`
#    takes its packages out of the build cache instead). Optimised outside the
#    compiler: a library has no main to anchor it, and the in-process -O2
#    would delete every body.
# ---------------------------------------------------------------------------
say "building the runtime archive"
SCALY_HOME="$NEW" "$NEW/libexec/scalyc" -S --no-prelude --no-tests \
  -o "$WORK/libscaly.ll" "$STDLIB/scaly.scaly" > "$WORK/libscaly.log" 2>&1 \
  || { tail -20 "$WORK/libscaly.log" >&2; die "emitting the standard library failed"; }
sed 's/^define linkonce_odr /define weak_odr /' "$WORK/libscaly.ll" > "$WORK/libscaly_weak.ll"
"$OPT" -O2 "$WORK/libscaly_weak.ll" -o "$WORK/libscaly_opt.bc" || die "optimising the standard library failed"
"$LLC" -relocation-model=pic -O2 -filetype=obj "$WORK/libscaly_opt.bc" -o "$WORK/libscaly.o" \
  || die "compiling the standard library failed"
"$AR" rcs "$NEW/lib/libscaly.a" "$WORK/libscaly.o" "$WORK/fcontext.o" "$WORK/eio.o" "$WORK/ctime.o" "$WORK/panic.o" \
  || die "ar failed"
fi   # built from the seed

# ---------------------------------------------------------------------------
# 7. Prove it, BEFORE anything installed is touched: a program run through
#    `scaly run`, and one built and run, by the programs of the new toolchain
#    where it stands. (The first build also compiles the standard library into
#    the cache -- its key holds no path, so the next build is quick.)
# ---------------------------------------------------------------------------
# The programs call `clang` to link and to compile a package's C files unless
# $SCALY_CC or $CC names another; say so only where `clang` is not what was
# found: the file CC in the toolchain, which a program reads as its SCALY_CC
# when the variable is not set.
if [ -n "$CC" ] && [ "$CC" != "clang" ]; then
  printf '%s\n' "$CC" > "$NEW/CC"
fi
say "checking the installation"
[ "$(cd "$WORK/check" && "$NEW/libexec/scaly" run hello.scaly 2>"$WORK/check/run.log")" = "Hello, World!" ] \
  || { tail -20 "$WORK/check/run.log" >&2; die "the installed scaly could not run a program"; }
if [ -n "$CC" ]; then
  ( cd "$WORK/check" && "$NEW/libexec/scaly" build hello.scaly -o hello ) > "$WORK/check/build.log" 2>&1 \
    || { tail -20 "$WORK/check/build.log" >&2; die "the installed scaly could not build a program"; }
  [ "$("$WORK/check/hello")" = "Hello, World!" ] || die "the program the installed scaly built does not run"
fi

# ---------------------------------------------------------------------------
# 8. The new toolchain takes the old one's place, and the three commands are
#    LINKS to its programs. A program finds the packages beside itself
#    (<toolchain>/libexec/<name> -> <toolchain>/packages, the link resolved
#    first), so nothing sets SCALY_HOME; a SCALY_HOME that is set still wins.
#    The links are relative: the whole prefix can be moved.
# ---------------------------------------------------------------------------
# an installation of the first layout (everything directly under the prefix)
if [ -f "$PREFIX/seed/scalyc.ll" ]; then
  rm -rf "$PREFIX/seed" "$PREFIX/libexec" "$PREFIX/lib" "$PREFIX/packages/scaly" "$PREFIX/LICENSE" "$PREFIX/VERSION"
  rmdir "$PREFIX/packages" 2>/dev/null || true
fi
rm -rf "$TOOLCHAIN"
mv "$NEW" "$TOOLCHAIN"

BINDIR="$PREFIX/bin"
mkdir -p "$BINDIR"
for name in scaly scalyc scalyls; do
  rm -f "$BINDIR/$name"     # the script of an earlier installation
  ln -s "../$(basename "$TOOLCHAIN")/libexec/$name" "$BINDIR/$name"
done

# What a BUILD needs and this machine lacks is fetched here, so that the
# newcomer's first `scaly build` works: Apple's Command Line Tools (the route
# Homebrew's installer takes: the marker file makes softwareupdate list them,
# and no dialog opens), or clang through apt. Both need administrator rights,
# so the one thing asked is the password, by sudo, on the terminal. Where that
# cannot be had -- no sudo, no terminal -- the note below stays.
if [ -z "$CC" ] && [ "${SCALY_BUILD_TOOLS:-install}" != skip ]; then
  SUDO=""
  ROOT=0
  if [ "$(id -u)" = 0 ]; then ROOT=1; elif have sudo; then SUDO="sudo"; fi
  if [ "$ROOT" = 1 ] || [ -n "$SUDO" ]; then
    if [ "$OS" = "Darwin" ]; then
      say "scaly build links with Apple's Command Line Tools, and they are not installed."
      say "fetching them now -- Apple's software under Apple's license, some minutes;"
      say "macOS asks for your password (SCALY_BUILD_TOOLS=skip leaves them out)."
      MARKER=/tmp/.com.apple.dt.CommandLineTools.installondemand.in-progress
      touch "$MARKER"
      LABEL="$(softwareupdate -l 2>/dev/null | sed -n 's/^.*Label: \(Command Line Tools.*\)$/\1/p' | tail -1)"
      if [ -n "$LABEL" ]; then
        $SUDO softwareupdate -i "$LABEL" --agree-to-license || true
      else
        say "softwareupdate lists no Command Line Tools"
      fi
      rm -f "$MARKER"
      if xcode-select -p >/dev/null 2>&1 && have clang; then CC=clang; fi
    elif have apt-get; then
      say "scaly build links with a C compiler, and none is installed."
      if [ -n "$SUDO" ]; then say "fetching clang now with apt -- sudo asks for your password (SCALY_BUILD_TOOLS=skip leaves it out)."
      else say "fetching clang now with apt (SCALY_BUILD_TOOLS=skip leaves it out)."; fi
      $SUDO apt-get install -y clang >/dev/null 2>&1 \
        || { $SUDO apt-get update >/dev/null 2>&1 && $SUDO apt-get install -y clang >/dev/null 2>&1; } || true
      if have clang; then CC=clang; fi
    fi
    if [ -n "$CC" ]; then say "a C compiler is there now: scaly build links"; fi
  fi
fi

if [ -z "$CC" ]; then
  say "NOTE: no C compiler found. scaly run, scaly test and the REPL work;"
  if [ "$OS" = "Darwin" ]; then
    say "      scaly build needs one to link:  xcode-select --install"
  else
    say "      scaly build needs one to link:  sudo apt install clang"
  fi
fi

# ---------------------------------------------------------------------------
# 9. Put the commands on PATH via the shell profile (unless already there).
# ---------------------------------------------------------------------------
case ":$PATH:" in
  *":$BINDIR:"*) say "$BINDIR is on your PATH" ;;
  *)
    if [ "${SCALY_NO_MODIFY_PATH:-0}" = "1" ]; then
      say "add this to your shell profile:  export PATH=\"$BINDIR:\$PATH\""
    else
      case "${SHELL:-}" in
        */zsh)  PROFILE="$HOME/.zshrc" ;;
        */bash) PROFILE="$HOME/.bashrc" ;;
        *)      PROFILE="$HOME/.profile" ;;
      esac
      LINE="export PATH=\"$BINDIR:\$PATH\"  # added by scaly-install"
      if [ -f "$PROFILE" ] && grep -qF "$BINDIR" "$PROFILE" 2>/dev/null; then
        say "$PROFILE already names $BINDIR"
      else
        printf '\n%s\n' "$LINE" >> "$PROFILE"
        say "added $BINDIR to PATH in $PROFILE"
      fi
      say "open a new terminal, or run:  export PATH=\"$BINDIR:\$PATH\""
    fi
    ;;
esac

cat <<EOF

scaly-install: done. Scaly $VERSION is installed in $PREFIX.
  Try it:
    printf 'print("Hello, World!")\\n' > hello.scaly
    scaly run hello.scaly              run it
    scaly build hello.scaly -o hello   build a program ...
    ./hello                            ... and run that
    scaly                              the REPL (:help, :quit)
  Uninstall:  rm -rf "$PREFIX"  (and remove the PATH line above)
EOF
