Files
GridFire/check.sh

392 lines
14 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# install.sh -- GridFire build-dependency checker.
#
# This script does NOT install anything. It detects your operating system,
# checks each build dependency, and prints the exact commands to run if
# something is missing or too old. Nothing on your system is modified.
#
# Dependencies checked:
# 1. Meson >= 1.6.0
# 2. Ninja (any recent)
# 3. CMake >= 3.16
# 4. pkg-config (or pkgconf)
# 5. A C and C++ toolchain: GCC >= 14 OR Clang >= 17 (either is fine),
# with a working C++23 standard library
# 6. Fortran (gfortran >= 14) -- only when --fortran is passed
#
# Supported platforms: Linux (apt / dnf / pacman / zypper) and macOS on
# Apple Silicon. macOS x86_64 and Windows are NOT supported.
#
# Usage:
# ./install.sh check core dependencies
# ./install.sh --fortran also check for a Fortran compiler
# ./install.sh --help show this help
set -u
MESON_MIN="1.6.0"
CMAKE_MIN="3.16"
GCC_MIN="14"
CLANG_MIN="17"
GFORTRAN_MIN="14"
if [ -t 1 ]; then
RED=$'\033[0;31m'; GREEN=$'\033[0;32m'; YELLOW=$'\033[1;33m'
BLUE=$'\033[0;34m'; BOLD=$'\033[1m'; NC=$'\033[0m'
else
RED=""; GREEN=""; YELLOW=""; BLUE=""; BOLD=""; NC=""
fi
ok() { printf ' %s[ OK ]%s %s\n' "$GREEN" "$NC" "$1"; }
bad() { printf ' %s[FAIL]%s %s\n' "$RED" "$NC" "$1"; }
warn() { printf ' %s[WARN]%s %s\n' "$YELLOW" "$NC" "$1"; }
info() { printf ' %s[INFO]%s %s\n' "$BLUE" "$NC" "$1"; }
hdr() { printf '\n%s%s%s\n' "$BOLD" "$1" "$NC"; }
FIX_NOTES=""
FAIL_COUNT=0
add_fix() { FIX_NOTES="${FIX_NOTES}$1\n"; }
note_fail() { FAIL_COUNT=$((FAIL_COUNT + 1)); }
WANT_FORTRAN=0
for arg in "$@"; do
case "$arg" in
--fortran) WANT_FORTRAN=1 ;;
-h|--help) sed -n '2,/^$/p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
*) printf '%sUnknown argument: %s%s\n' "$RED" "$arg" "$NC"; exit 2 ;;
esac
done
version_ge() {
local a b ai bi n i
IFS='.' read -ra a <<< "$1"
IFS='.' read -ra b <<< "$2"
n=${#a[@]}; [ ${#b[@]} -gt "$n" ] && n=${#b[@]}
for ((i=0; i<n; i++)); do
ai=${a[i]:-0}; bi=${b[i]:-0}
ai=${ai%%[!0-9]*}; bi=${bi%%[!0-9]*}
ai=${ai:-0}; bi=${bi:-0}
if ((10#$ai > 10#$bi)); then return 0; fi
if ((10#$ai < 10#$bi)); then return 1; fi
done
return 0
}
extract_version() {
printf '%s' "$1" | grep -oE '[0-9]+(\.[0-9]+){0,3}' | head -n1
}
OS="$(uname -s)"
ARCH="$(uname -m)"
PKG_MGR=""
DISTRO_NAME=""
detect_platform() {
case "$OS" in
Darwin)
if [ "$ARCH" != "arm64" ]; then
printf '%sUnsupported platform.%s\n' "$RED" "$NC"
printf 'GridFire supports macOS on Apple Silicon (arm64) only; detected macOS on %s.\n' "$ARCH"
printf 'macOS x86_64 (Intel) is not supported.\n'
exit 1
fi
DISTRO_NAME="macOS (Apple Silicon)"
if command -v brew >/dev/null 2>&1; then
PKG_MGR="brew"
else
PKG_MGR="brew-missing"
fi
;;
Linux)
if command -v apt-get >/dev/null 2>&1; then PKG_MGR="apt"
elif command -v dnf >/dev/null 2>&1; then PKG_MGR="dnf"
elif command -v pacman >/dev/null 2>&1; then PKG_MGR="pacman"
elif command -v zypper >/dev/null 2>&1; then PKG_MGR="zypper"
else PKG_MGR="unknown"; fi
if [ -r /etc/os-release ]; then
# shellcheck disable=SC1091
DISTRO_NAME="$(. /etc/os-release; printf '%s' "${PRETTY_NAME:-Linux}")"
else
DISTRO_NAME="Linux"
fi
;;
*)
printf '%sUnsupported platform: %s.%s\n' "$RED" "$OS" "$NC"
printf 'GridFire supports Linux and macOS (Apple Silicon). Windows is not supported.\n'
printf 'On Windows, use WSL2 with a supported Linux distribution.\n'
exit 1
;;
esac
}
pkg_cmd() {
case "$PKG_MGR" in
apt)
case "$1" in
ninja) echo "sudo apt install ninja-build" ;;
cmake) echo "sudo apt install cmake" ;;
pkgconfig) echo "sudo apt install pkg-config" ;;
gcc) echo "sudo apt install g++-14 gcc-14" ;;
clang) echo "sudo apt install clang-18 libstdc++-14-dev" ;;
gfortran) echo "sudo apt install gfortran-14" ;;
esac ;;
dnf)
case "$1" in
ninja) echo "sudo dnf install ninja-build" ;;
cmake) echo "sudo dnf install cmake" ;;
pkgconfig) echo "sudo dnf install pkgconf-pkg-config" ;;
gcc) echo "sudo dnf install gcc-c++" ;;
clang) echo "sudo dnf install clang" ;;
gfortran) echo "sudo dnf install gcc-gfortran" ;;
esac ;;
pacman)
case "$1" in
ninja) echo "sudo pacman -S ninja" ;;
cmake) echo "sudo pacman -S cmake" ;;
pkgconfig) echo "sudo pacman -S pkgconf" ;;
gcc) echo "sudo pacman -S gcc" ;;
clang) echo "sudo pacman -S clang" ;;
gfortran) echo "sudo pacman -S gcc-fortran" ;;
esac ;;
zypper)
case "$1" in
ninja) echo "sudo zypper install ninja" ;;
cmake) echo "sudo zypper install cmake" ;;
pkgconfig) echo "sudo zypper install pkg-config" ;;
gcc) echo "sudo zypper install gcc14-c++" ;;
clang) echo "sudo zypper install clang" ;;
gfortran) echo "sudo zypper install gcc14-fortran" ;;
esac ;;
brew|brew-missing)
case "$1" in
ninja) echo "brew install ninja" ;;
cmake) echo "brew install cmake" ;;
pkgconfig) echo "brew install pkg-config" ;;
gcc) echo "brew install gcc" ;;
clang) echo "brew install llvm # then use /opt/homebrew/opt/llvm/bin/clang++" ;;
gfortran) echo "brew install gcc # provides gfortran" ;;
esac ;;
*)
echo "(install '$1' using your system package manager)" ;;
esac
}
check_meson() {
if command -v meson >/dev/null 2>&1; then
local v; v="$(extract_version "$(meson --version 2>/dev/null)")"
if [ -n "$v" ] && version_ge "$v" "$MESON_MIN"; then
ok "Meson $v (>= $MESON_MIN)"
else
bad "Meson ${v:-?} found but GridFire needs >= $MESON_MIN"
note_fail
add_fix "Meson upgrade (isolated, recommended):\n pipx upgrade meson || pipx install meson\n or with pip:\n python3 -m pip install --user --upgrade meson"
fi
else
bad "Meson not found (need >= $MESON_MIN)"
note_fail
add_fix "Install Meson (isolated, recommended):\n pipx install meson\n If pipx is unavailable:\n python3 -m pip install --user meson\n (pipx itself: ${PKG_MGR/brew-missing/brew} install pipx; on Linux: python3 -m pip install --user pipx)"
fi
}
check_ninja() {
if command -v ninja >/dev/null 2>&1; then
ok "Ninja $(extract_version "$(ninja --version 2>/dev/null)")"
elif command -v samu >/dev/null 2>&1; then
ok "samurai (ninja-compatible) found"
else
bad "Ninja not found"
note_fail
add_fix "Install Ninja (isolated, recommended):\n pipx inject meson ninja # if meson via pipx\n or:\n python3 -m pip install --user ninja\n or via system packages:\n $(pkg_cmd ninja)"
fi
}
check_cmake() {
if command -v cmake >/dev/null 2>&1; then
local v; v="$(extract_version "$(cmake --version 2>/dev/null)")"
if [ -n "$v" ] && version_ge "$v" "$CMAKE_MIN"; then
ok "CMake $v (>= $CMAKE_MIN)"
else
bad "CMake ${v:-?} found but >= $CMAKE_MIN required (SUNDIALS/CppAD subprojects)"
note_fail
add_fix "Upgrade CMake:\n $(pkg_cmd cmake)\n or isolated: python3 -m pip install --user --upgrade cmake"
fi
else
bad "CMake not found (required to build the SUNDIALS and CppAD subprojects)"
note_fail
add_fix "Install CMake:\n $(pkg_cmd cmake)\n or isolated: python3 -m pip install --user cmake"
fi
}
check_pkgconfig() {
if command -v pkg-config >/dev/null 2>&1; then
ok "pkg-config $(extract_version "$(pkg-config --version 2>/dev/null)")"
elif command -v pkgconf >/dev/null 2>&1; then
ok "pkgconf $(extract_version "$(pkgconf --version 2>/dev/null)") (pkg-config compatible)"
else
bad "pkg-config not found"
note_fail
add_fix "Install pkg-config:\n $(pkg_cmd pkgconfig)"
fi
}
probe_cxx23() {
local cxx="$1" dir src out rc
command -v "$cxx" >/dev/null 2>&1 || return 2
dir="$(mktemp -d 2>/dev/null)" || return 3
src="$dir/gf_probe.cpp"
out="$dir/gf_probe.out"
cat > "$src" <<'EOF'
#include <print>
#include <format>
#include <expected>
int main() {
std::println("ok {}", 23);
std::expected<int, int> test;
}
EOF
"$cxx" -std=c++23 "$src" -o "$out" >/dev/null 2>&1
rc=$?
rm -rf "$dir"
return $rc
}
check_toolchain() {
local found_ok=0 detail=""
local candidates=()
[ -n "${CXX:-}" ] && candidates+=("$CXX")
candidates+=(clang++ clang++-21 clang++-20 clang++-19 clang++-18 clang++-17 g++ g++-16 g++-15 g++-14)
if [ "$OS" = "Darwin" ]; then
candidates+=(/opt/homebrew/opt/llvm/bin/clang++ /usr/local/opt/llvm/bin/clang++)
fi
local seen="" cxx ver id minreq
for cxx in "${candidates[@]}"; do
command -v "$cxx" >/dev/null 2>&1 || continue
local rp; rp="$(command -v "$cxx")"
case " $seen " in *" $rp "*) continue ;; esac
seen="$seen $rp"
ver="$("$cxx" --version 2>/dev/null | head -n1)"
if printf '%s' "$ver" | grep -qiE 'clang'; then
id="clang"; minreq="$CLANG_MIN"
elif printf '%s' "$ver" | grep -qiE 'g\+\+|gcc|free software foundation'; then
id="gcc"; minreq="$GCC_MIN"
else
id="unknown"; minreq=""
fi
local vnum; vnum="$(extract_version "$ver")"
if probe_cxx23 "$cxx"; then
if [ -n "$minreq" ] && [ -n "$vnum" ] && ! version_ge "$vnum" "$minreq"; then
warn "$cxx ($id $vnum): C++23 probe passed but below tested floor ($minreq) -- usable, upgrade if you hit errors"
else
ok "$cxx ($id ${vnum:-?}): C++23 standard library working"
fi
found_ok=1
[ -z "${WORKING_CXX:-}" ] && WORKING_CXX="$rp"
else
detail="${detail}\n - $cxx ($id ${vnum:-?}): present but failed the C++23 compile probe"
fi
done
local have_cc=0
for c in "${CC:-}" cc gcc clang; do
[ -n "$c" ] && command -v "$c" >/dev/null 2>&1 && { have_cc=1; break; }
done
if [ "$found_ok" -eq 1 ] && [ "$have_cc" -eq 1 ]; then
ok "C toolchain present"
[ -n "${WORKING_CXX:-}" ] && info "Suggested: CXX=$WORKING_CXX meson setup build"
return
fi
if [ "$found_ok" -ne 1 ]; then
bad "No C++ compiler with working C++23 support found (need GCC >= $GCC_MIN or Clang >= $CLANG_MIN)"
note_fail
[ -n "$detail" ] && printf ' Compilers seen but rejected:%b\n' "$detail"
fi
if [ "$have_cc" -ne 1 ]; then
bad "No C compiler found"
note_fail
fi
local fix="Install a C/C++ toolchain (Clang >= $CLANG_MIN or GCC >= $GCC_MIN):\n $(pkg_cmd clang)\n or GCC:\n $(pkg_cmd gcc)"
# local fix="Install a C/C++ toolchain (GCC >= $GCC_MIN or Clang >= $CLANG_MIN):\n $(pkg_cmd gcc)\n or Clang:\n $(pkg_cmd clang)"
if [ "$OS" = "Linux" ]; then
fix="$fix\n Note: on Linux, Clang uses the system libstdc++ -- if a new Clang fails the\n C++23 probe, install a newer libstdc++ (e.g. via g++-14) and Clang will use it."
elif [ "$OS" = "Darwin" ]; then
fix="$fix\n Apple Clang: update Xcode / Command Line Tools to Xcode 16+\n xcode-select --install (or update via the App Store)"
fi
add_fix "$fix"
}
check_fortran() {
[ "$WANT_FORTRAN" -eq 1 ] || return 0
if command -v gfortran >/dev/null 2>&1; then
local v; v="$(extract_version "$(gfortran --version 2>/dev/null | head -n1)")"
if [ -n "$v" ] && version_ge "$v" "$GFORTRAN_MIN"; then
ok "gfortran $v (>= $GFORTRAN_MIN)"
else
bad "gfortran ${v:-?} found but >= $GFORTRAN_MIN required"
note_fail
add_fix "Upgrade gfortran:\n $(pkg_cmd gfortran)"
fi
else
bad "gfortran not found (required by --fortran / -Dbuild_fortran=true)"
note_fail
add_fix "Install gfortran:\n $(pkg_cmd gfortran)\n (GridFire only supports gfortran; other Fortran compilers need -Dunsafe_fortran=true)"
fi
}
printf '%s======================================================%s\n' "$BOLD" "$NC"
printf '%s GridFire build-dependency check%s\n' "$BOLD" "$NC"
printf '%s======================================================%s\n' "$BOLD" "$NC"
detect_platform
info "Platform: $DISTRO_NAME ($ARCH)"
case "$PKG_MGR" in
brew-missing)
warn "Homebrew not found. Install it first: https://brew.sh"
warn "All macOS remediation commands below assume Homebrew." ;;
unknown)
warn "Could not identify a supported package manager (apt/dnf/pacman/zypper)." ;;
*) info "Package manager: $PKG_MGR" ;;
esac
hdr "Build tools"
check_meson
check_ninja
check_cmake
check_pkgconfig
hdr "Compiler toolchain"
check_toolchain
if [ "$WANT_FORTRAN" -eq 1 ]; then
hdr "Fortran (requested via --fortran)"
check_fortran
fi
printf '\n%s======================================================%s\n' "$BOLD" "$NC"
if [ "$FAIL_COUNT" -eq 0 ]; then
printf '%s All dependencies satisfied.%s\n' "$GREEN" "$NC"
printf '%s======================================================%s\n' "$BOLD" "$NC"
printf '\nYou can build GridFire with:\n\n'
if [ -n "${WORKING_CXX:-}" ]; then
printf ' CXX=%s meson setup build\n' "$WORKING_CXX"
else
printf ' meson setup build\n'
fi
printf ' meson compile -C build\n'
printf ' sudo meson install -C build\n\n'
exit 0
else
printf '%s %d dependency problem(s) found -- see below.%s\n' "$RED" "$FAIL_COUNT" "$NC"
printf '%s======================================================%s\n' "$BOLD" "$NC"
printf '\n%sThis script does not install anything. Run the commands below yourself:%s\n\n' "$BOLD" "$NC"
printf '%b\n' "$FIX_NOTES"
printf 'Re-run %s./install.sh%s after installing to confirm.\n\n' "$BOLD" "$NC"
exit 1
fi