1172 lines
45 KiB
Bash
Executable File
1172 lines
45 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# install.sh - Comprehensive interactive installation script for GridFire.
|
|
#
|
|
# This script performs the following actions:
|
|
# 1. Checks for essential system dependencies against required minimum versions.
|
|
# 2. If run with the --tui flag, it provides a comprehensive text-based user interface
|
|
# to select and install dependencies, configure the build, and run build steps.
|
|
# 3. If run without flags, it prompts the user interactively for each missing dependency.
|
|
# 4. Provides detailed installation instructions for various Linux distributions and macOS.
|
|
# 5. Once all dependencies are met, it can run the meson setup, compile, install, and test commands.
|
|
# 6. Logs all operations to a file for easy debugging.
|
|
|
|
set -o pipefail
|
|
|
|
# --- Default Configuration ---
|
|
LOGFILE="GridFire_Installer.log"
|
|
NOTES_FILE="notes.txt"
|
|
CONFIG_FILE="gridfire_build.conf"
|
|
MIN_GCC_VER="13.0.0"
|
|
MIN_CLANG_VER="16.0.0"
|
|
MIN_MESON_VER="1.5.0"
|
|
BOOST_CHECKED=false
|
|
BOOST_OKAY=true
|
|
|
|
# --- Build Configuration Globals ---
|
|
BUILD_DIR="build"
|
|
INSTALL_PREFIX="/usr/local"
|
|
MESON_BUILD_TYPE="debug" # Default to debug to match Meson's default and avoid optimization warnings
|
|
MESON_LOG_LEVEL="info"
|
|
MESON_PKG_CONFIG="true"
|
|
MESON_NUM_CORES=$(getconf _NPROCESSORS_ONLN 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 1)
|
|
C_COMPILER=""
|
|
CC_COMPILER=""
|
|
FC_COMPILER=""
|
|
declare -A VALID_COMPILERS
|
|
declare -A MESON_ADVANCED_OPTS
|
|
# --- Initialize default advanced options ---
|
|
MESON_ADVANCED_OPTS["b_lto"]="false"
|
|
MESON_ADVANCED_OPTS["strip"]="false"
|
|
MESON_ADVANCED_OPTS["unity"]="off"
|
|
MESON_ADVANCED_OPTS["warning_level"]="1"
|
|
MESON_ADVANCED_OPTS["backend"]="ninja"
|
|
MESON_ADVANCED_OPTS["werror"]="false"
|
|
MESON_ADVANCED_OPTS["b_pch"]="true"
|
|
MESON_ADVANCED_OPTS["b_coverage"]="false"
|
|
MESON_ADVANCED_OPTS["default_library"]="shared"
|
|
|
|
|
|
# --- ANSI Color Codes ---
|
|
RED="\033[0;31m"
|
|
GREEN="\033[0;32m"
|
|
YELLOW="\033[0;33m"
|
|
BLUE="\033[0;34m"
|
|
NC="\033[0m" # No Color
|
|
|
|
# --- Logging and Utility Functions ---
|
|
|
|
# Log a message to stdout and to the log file.
|
|
log() {
|
|
local message="$1"
|
|
# Print the colored message to the terminal.
|
|
echo -e "${message}"
|
|
# Strip ANSI color codes and append to the log file.
|
|
echo -e "${message}" | sed 's/\x1B\[[0-9;]*[mK]//g' >> "$LOGFILE"
|
|
}
|
|
|
|
# Version comparison function. Returns 0 if v1=v2, 1 if v1>v2, 2 if v1<v2
|
|
vercomp() {
|
|
local v1=$1 v2=$2
|
|
if [[ "$v1" == "$v2" ]]; then return 0; fi
|
|
local IFS=.
|
|
read -ra v1_parts <<< "$v1"
|
|
read -ra v2_parts <<< "$v2"
|
|
|
|
local i
|
|
# Pad the shorter version array with zeros
|
|
local len1=${#v1_parts[@]}
|
|
local len2=${#v2_parts[@]}
|
|
if (( len1 > len2 )); then
|
|
for ((i=len2; i<len1; i++)); do v2_parts[i]=0; done
|
|
elif (( len2 > len1 )); then
|
|
for ((i=len1; i<len2; i++)); do v1_parts[i]=0; done
|
|
fi
|
|
|
|
for i in "${!v1_parts[@]}"; do
|
|
local p1=${v1_parts[i]}
|
|
local p2=${v2_parts[i]}
|
|
|
|
# Ensure parts are integers before comparison
|
|
if ! [[ "$p1" =~ ^[0-9]+$ ]]; then p1=0; fi
|
|
if ! [[ "$p2" =~ ^[0-9]+$ ]]; then p2=0; fi
|
|
|
|
if (( p1 > p2 )); then return 1; fi
|
|
if (( p1 < p2 )); then return 2; fi
|
|
done
|
|
|
|
return 0
|
|
}
|
|
|
|
|
|
# Show the help message and exit.
|
|
show_help() {
|
|
echo "Usage: ./install.sh [options]"
|
|
echo
|
|
echo "This script checks for dependencies, installs them, and builds the project."
|
|
echo
|
|
echo "Options:"
|
|
echo " --tui Run in Text-based User Interface mode for interactive dependency installation and build control."
|
|
echo " --help, -h Show this help message and exit."
|
|
echo " --clean Remove the build directory and log file before starting."
|
|
echo " --config FILE Load a configuration file on startup."
|
|
echo
|
|
echo "The script will automatically detect your OS and suggest the correct package manager commands."
|
|
echo "All output is logged to ${LOGFILE}."
|
|
}
|
|
|
|
# --- OS Detection ---
|
|
OS_NAME=$(uname -s)
|
|
DISTRO_ID="unknown"
|
|
if [ -f /etc/os-release ]; then
|
|
. /etc/os-release
|
|
DISTRO_ID=$ID
|
|
fi
|
|
if [[ "$OS_NAME" == "Darwin" ]]; then
|
|
OS_NAME="macOS"
|
|
fi
|
|
|
|
# --- Dependency Check Functions ---
|
|
|
|
check_command() {
|
|
command -v "$1" &>/dev/null
|
|
}
|
|
|
|
set_compilers() {
|
|
if [[ "$CC_COMPILER" == *"clang++"* ]]; then
|
|
C_COMPILER=$(echo "$CC_COMPILER" | sed 's/clang++/clang/')
|
|
# Try to find a corresponding flang or fallback to gfortran
|
|
local fc_ver=$(echo "$C_COMPILER" | grep -oE '[0-9]+')
|
|
if check_command "flang-$fc_ver"; then
|
|
FC_COMPILER="flang-$fc_ver"
|
|
elif check_command "flang"; then
|
|
FC_COMPILER="flang"
|
|
elif check_command "gfortran"; then
|
|
FC_COMPILER="gfortran"
|
|
else
|
|
FC_COMPILER=""
|
|
fi
|
|
elif [[ "$CC_COMPILER" == *"g++"* ]]; then
|
|
C_COMPILER=$(echo "$CC_COMPILER" | sed 's/g++/gcc/')
|
|
FC_COMPILER=$(echo "$CC_COMPILER" | sed 's/g++/gfortran/')
|
|
if ! check_command "$FC_COMPILER"; then
|
|
# Fallback to generic gfortran if versioned one not found
|
|
if check_command "gfortran"; then
|
|
FC_COMPILER="gfortran"
|
|
else
|
|
FC_COMPILER=""
|
|
fi
|
|
fi
|
|
else
|
|
C_COMPILER=""
|
|
FC_COMPILER=""
|
|
fi
|
|
}
|
|
|
|
check_compiler() {
|
|
VALID_COMPILERS=()
|
|
local potential_gccs=("g++-14" "g++-13" "g++")
|
|
local potential_clangs=("clang++-17" "clang++-16" "clang++")
|
|
|
|
for cc in "${potential_gccs[@]}"; do
|
|
if check_command "$cc"; then
|
|
local ver; ver=$($cc -dumpversion | grep -oE '[0-9]+(\.[0-9]+)*' | head -n1)
|
|
if [[ -n "$ver" ]]; then
|
|
vercomp "$ver" "$MIN_GCC_VER"
|
|
if [[ $? -ne 2 ]]; then
|
|
VALID_COMPILERS["g++ ($ver)"]="$cc"
|
|
break # Found a valid one, stop searching for g++
|
|
fi
|
|
fi
|
|
fi
|
|
done
|
|
|
|
for cc in "${potential_clangs[@]}"; do
|
|
if check_command "$cc"; then
|
|
local ver; ver=$($cc --version | head -n1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -n1)
|
|
if [[ -n "$ver" ]]; then
|
|
vercomp "$ver" "$MIN_CLANG_VER"
|
|
if [[ $? -ne 2 ]]; then
|
|
VALID_COMPILERS["clang++ ($ver)"]="$cc"
|
|
break # Found a valid one
|
|
fi
|
|
fi
|
|
fi
|
|
done
|
|
|
|
if [ ${#VALID_COMPILERS[@]} -gt 0 ]; then
|
|
if ! [[ " ${VALID_COMPILERS[*]} " =~ " ${CC_COMPILER} " ]]; then
|
|
# Correctly get the first value from the associative array
|
|
local keys=("${!VALID_COMPILERS[@]}")
|
|
local first_key="${keys[0]}"
|
|
CC_COMPILER="${VALID_COMPILERS[$first_key]}"
|
|
fi
|
|
set_compilers
|
|
log "${GREEN}[OK] Found valid C++ compiler(s). Using C: '${C_COMPILER}', C++: '${CC_COMPILER}', FC: '${FC_COMPILER}'.${NC}"
|
|
return 0
|
|
else
|
|
log "${RED}[FAIL] No valid C++ compiler found (GCC >= ${MIN_GCC_VER} or Clang >= ${MIN_CLANG_VER}).${NC}"
|
|
CC_COMPILER=""
|
|
C_COMPILER=""
|
|
FC_COMPILER=""
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
check_python_dev() {
|
|
if check_command python3 && python3-config --includes &>/dev/null; then
|
|
log "${GREEN}[OK] Found Python 3 development headers.${NC}"
|
|
return 0
|
|
else
|
|
log "${RED}[FAIL] Python 3 development headers not found.${NC}"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
check_meson_python() {
|
|
if python3 -c "import mesonpy" &>/dev/null; then
|
|
log "${GREEN}[OK] Found meson-python package.${NC}"
|
|
return 0
|
|
else
|
|
log "${RED}[FAIL] meson-python package not found.${NC}"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
check_cmake() {
|
|
if check_command cmake; then
|
|
log "${GREEN}[OK] Found CMake: $(cmake --version | head -n1)${NC}"
|
|
return 0
|
|
else
|
|
log "${RED}[FAIL] CMake not found.${NC}"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
check_meson() {
|
|
if check_command meson; then
|
|
local ver; ver=$(meson --version)
|
|
vercomp "$ver" "$MIN_MESON_VER"
|
|
if [[ $? -ne 2 ]]; then
|
|
log "${GREEN}[OK] Found Meson ${ver}.${NC}"
|
|
return 0
|
|
else
|
|
log "${RED}[FAIL] Meson version ${ver} is too old. Need >= ${MIN_MESON_VER}.${NC}"
|
|
return 1
|
|
fi
|
|
else
|
|
log "${RED}[FAIL] Meson not found.${NC}"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
check_boost() {
|
|
log "${BLUE}[Info] Performing comprehensive check for compatible Boost library...${NC}"
|
|
if [ -z "$CC_COMPILER" ]; then
|
|
log "${YELLOW}[Warn] C++ compiler not set, skipping comprehensive Boost check.${NC}"
|
|
return 1
|
|
fi
|
|
|
|
local test_dir="boost-compile-test"
|
|
rm -rf "$test_dir" && mkdir -p "$test_dir"
|
|
|
|
# Create the test C++ file that uses problematic headers
|
|
cat > "$test_dir/test_boost.cpp" <<EOF
|
|
#include <boost/numeric/ublas/vector.hpp>
|
|
#include <boost/numeric/odeint.hpp>
|
|
|
|
using state_type = boost::numeric::ublas::vector<double>;
|
|
|
|
void dummy_system(const state_type &x, state_type &dxdt, double t) {
|
|
if (x.size() > 0 && dxdt.size() > 0) {
|
|
dxdt[0] = 1.0;
|
|
}
|
|
}
|
|
|
|
int main() {
|
|
boost::numeric::ublas::vector<double> v(3);
|
|
v[0] = 1.23;
|
|
state_type x(1);
|
|
x[0] = 1.0;
|
|
boost::numeric::odeint::runge_kutta4<state_type> stepper;
|
|
stepper.do_step(dummy_system, x, 0.0, 0.1);
|
|
return 0;
|
|
}
|
|
EOF
|
|
|
|
# Create the test meson.build file
|
|
cat > "$test_dir/meson.build" <<EOF
|
|
project('boost-compile-check', 'cpp', default_options: ['cpp_std=c++23'])
|
|
boost_dep = dependency('boost', required: true)
|
|
executable('test_boost', 'test_boost.cpp', dependencies: [boost_dep])
|
|
EOF
|
|
|
|
log "${BLUE}[Info] Attempting to compile test project against Boost with C++23...${NC}"
|
|
# Use the globally selected compilers, pipe stdout and stderr to log for debugging
|
|
if CC="${C_COMPILER}" CXX="${CC_COMPILER}" meson setup "$test_dir/build" "$test_dir" >> "$LOGFILE" 2>&1 && meson compile -C "$test_dir/build" >> "$LOGFILE" 2>&1; then
|
|
log "${GREEN}[Success] Boost library is compatible with the current compiler and C++23 standard.${NC}"
|
|
rm -rf "$test_dir"
|
|
return 0
|
|
else
|
|
log "${RED}[FAIL] System Boost library is NOT compatible with C++23 or is missing required components.${NC}"
|
|
log "${YELLOW}[Info] This is common on systems like Ubuntu with older Boost versions. A manual install of a newer Boost is likely required.${NC}"
|
|
rm -rf "$test_dir"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
|
|
# --- Dependency Installation Functions ---
|
|
|
|
get_compiler_install_cmd() {
|
|
local compiler_to_install="$1" # e.g., g++ or clang++
|
|
local cmd=""
|
|
case "$OS_NAME" in
|
|
"macOS")
|
|
local brew_cmd; brew_cmd=$(command -v brew)
|
|
case "$compiler_to_install" in
|
|
"g++") cmd="$brew_cmd install gcc" ;;
|
|
"clang++") cmd="$brew_cmd install llvm" ;; # llvm provides clang
|
|
esac
|
|
;;
|
|
"Linux")
|
|
case "$DISTRO_ID" in
|
|
"ubuntu"|"debian"|"linuxmint")
|
|
case "$compiler_to_install" in
|
|
"g++") cmd="sudo apt-get install -y g++-13 gfortran-13" ;;
|
|
"clang++") cmd="sudo apt-get install -y clang-16" ;;
|
|
esac
|
|
;;
|
|
"fedora")
|
|
# Fedora usually has recent versions in main repos
|
|
case "$compiler_to_install" in
|
|
"g++") cmd="sudo dnf install -y gcc-c++ gcc-gfortran" ;;
|
|
"clang++") cmd="sudo dnf install -y clang" ;;
|
|
esac
|
|
;;
|
|
"arch"|"manjaro")
|
|
# Arch is rolling release, should be fine
|
|
case "$compiler_to_install" in
|
|
"g++") cmd="sudo pacman -S --noconfirm gcc gcc-fortran" ;;
|
|
"clang++") cmd="sudo pacman -S --noconfirm clang" ;;
|
|
esac
|
|
;;
|
|
esac
|
|
;;
|
|
esac
|
|
echo "$cmd"
|
|
}
|
|
|
|
|
|
get_install_cmd() {
|
|
local dep_name="$1"
|
|
local cmd=""
|
|
case "$OS_NAME" in
|
|
"macOS")
|
|
[ ! -f /opt/homebrew/bin/brew ] && [ ! -f /usr/local/bin/brew ] && log "${YELLOW}Homebrew not found. Please install from https://brew.sh${NC}"
|
|
local brew_cmd
|
|
brew_cmd=$(command -v brew)
|
|
case "$dep_name" in
|
|
"compiler") cmd="$brew_cmd install gcc llvm" ;; # Install both
|
|
"python-dev") cmd="$brew_cmd install python3" ;;
|
|
"meson-python") cmd="python3 -m pip install meson-python" ;;
|
|
"meson") cmd="python3 -m pip install --upgrade meson" ;;
|
|
"cmake") cmd="$brew_cmd install cmake" ;;
|
|
"boost") cmd="$brew_cmd install boost" ;;
|
|
"dialog") cmd="$brew_cmd install dialog" ;;
|
|
esac
|
|
;;
|
|
"Linux")
|
|
case "$DISTRO_ID" in
|
|
"ubuntu"|"debian"|"linuxmint")
|
|
case "$dep_name" in
|
|
"compiler") cmd="sudo apt-get install -y g++-13 gfortran-13 clang-16" ;;
|
|
"python-dev") cmd="sudo apt-get install -y python3-dev" ;;
|
|
"meson-python") cmd="python3 -m pip install meson-python" ;;
|
|
"meson") cmd="python3 -m pip install --upgrade meson" ;;
|
|
"cmake") cmd="sudo apt-get install -y cmake" ;;
|
|
"boost") cmd="sudo apt-get install -y libboost-all-dev" ;;
|
|
"dialog") cmd="sudo apt-get install -y dialog" ;;
|
|
esac
|
|
;;
|
|
"fedora")
|
|
case "$dep_name" in
|
|
"compiler") cmd="sudo dnf install -y gcc-c++ gcc-gfortran clang" ;;
|
|
"python-dev") cmd="sudo dnf install -y python3-devel" ;;
|
|
"meson-python") cmd="python3 -m pip install meson-python" ;;
|
|
"meson") cmd="python3 -m pip install --upgrade meson" ;;
|
|
"cmake") cmd="sudo dnf install -y cmake" ;;
|
|
"boost") cmd="sudo dnf install -y boost-devel" ;;
|
|
"dialog") cmd="sudo dnf install -y dialog" ;;
|
|
esac
|
|
;;
|
|
"arch"|"manjaro")
|
|
case "$dep_name" in
|
|
"compiler") cmd="sudo pacman -S --noconfirm gcc gcc-fortran clang" ;;
|
|
"python-dev") cmd="sudo pacman -S --noconfirm python" ;;
|
|
"meson-python") cmd="python3 -m pip install meson-python" ;;
|
|
"meson") cmd="python3 -m pip install --upgrade meson" ;;
|
|
"cmake") cmd="sudo pacman -S --noconfirm cmake" ;;
|
|
"boost") cmd="sudo pacman -S --noconfirm boost" ;;
|
|
"dialog") cmd="sudo pacman -S --noconfirm dialog" ;;
|
|
esac
|
|
;;
|
|
*) log "${YELLOW}[Warn] Unsupported Linux distribution: ${DISTRO_ID}.${NC}" ;;
|
|
esac
|
|
;;
|
|
*) log "${YELLOW}[Warn] Unsupported OS: ${OS_NAME}.${NC}" ;;
|
|
esac
|
|
echo "$cmd"
|
|
}
|
|
|
|
# --- Build Functions ---
|
|
|
|
run_meson_setup() {
|
|
log "\n${BLUE}--- Configuring Meson Build ---${NC}"
|
|
if [ ! -f "meson.build" ]; then
|
|
log "${RED}[FATAL] meson.build file not found. Cannot proceed.${NC}"; return 1;
|
|
fi
|
|
if [ -z "$CC_COMPILER" ] || [ -z "$C_COMPILER" ]; then
|
|
log "${RED}[FATAL] No valid C/C++ compiler selected. Configure one first.${NC}"; return 1;
|
|
fi
|
|
local reconfigure_flag=""
|
|
if [ -d "$BUILD_DIR" ]; then
|
|
reconfigure_flag="--reconfigure"
|
|
log "${YELLOW}[Info] Existing build directory found. Will reconfigure.${NC}"
|
|
fi
|
|
|
|
local meson_opts=()
|
|
meson_opts+=("-Dbuildtype=${MESON_BUILD_TYPE}")
|
|
meson_opts+=("-Dlog_level=${MESON_LOG_LEVEL}")
|
|
meson_opts+=("-Dpkg-config=${MESON_PKG_CONFIG}")
|
|
meson_opts+=("--prefix=${INSTALL_PREFIX}")
|
|
|
|
# Add advanced options
|
|
for key in "${!MESON_ADVANCED_OPTS[@]}"; do
|
|
meson_opts+=("-D${key}=${MESON_ADVANCED_OPTS[$key]}")
|
|
done
|
|
|
|
log "${BLUE}[Info] Using C compiler: ${C_COMPILER}${NC}"
|
|
log "${BLUE}[Info] Using C++ compiler: ${CC_COMPILER}${NC}"
|
|
log "${BLUE}[Info] Using Fortran compiler: ${FC_COMPILER}${NC}"
|
|
log "${BLUE}[Info] Running meson setup with options: ${meson_opts[*]}${NC}"
|
|
# Set CC, CXX, and FC environment variables for the meson command
|
|
if ! CC="${C_COMPILER}" CXX="${CC_COMPILER}" FC="${FC_COMPILER}" meson setup "${BUILD_DIR}" "${meson_opts[@]}" ${reconfigure_flag}; then
|
|
log "${RED}[FATAL] Meson setup failed. See log for details.${NC}"; return 1;
|
|
fi
|
|
log "${GREEN}[Success] Meson setup complete.${NC}"
|
|
}
|
|
|
|
run_meson_compile() {
|
|
log "\n${BLUE}--- Compiling Project ---${NC}"
|
|
if [ ! -d "$BUILD_DIR" ]; then
|
|
log "${RED}[FATAL] Build directory not found. Run setup first.${NC}"; return 1;
|
|
fi
|
|
log "${BLUE}[Info] Running meson compile with ${MESON_NUM_CORES} cores...${NC}"
|
|
if ! meson compile -C "${BUILD_DIR}" -j "${MESON_NUM_CORES}"; then
|
|
log "${RED}[FATAL] Meson compile failed. See log for details.${NC}"; return 1;
|
|
fi
|
|
log "${GREEN}[Success] Meson compile complete.${NC}"
|
|
}
|
|
|
|
run_meson_install() {
|
|
log "\n${BLUE}--- Installing Project ---${NC}"
|
|
if [ ! -d "$BUILD_DIR" ]; then
|
|
log "${RED}[FATAL] Build directory not found. Run setup and compile first.${NC}"; return 1;
|
|
fi
|
|
log "${BLUE}[Info] Running meson install (prefix: ${INSTALL_PREFIX})...${NC}"
|
|
if ! sudo meson install -C "${BUILD_DIR}"; then
|
|
log "${RED}[FATAL] Meson install failed. See log for details.${NC}"; return 1;
|
|
fi
|
|
log "${GREEN}[Success] Meson install complete.${NC}"
|
|
}
|
|
|
|
run_meson_tests() {
|
|
log "\n${BLUE}--- Running Tests ---${NC}"
|
|
if [ ! -d "$BUILD_DIR" ]; then
|
|
log "${RED}[FATAL] Build directory not found. Run setup and compile first.${NC}"; return 1;
|
|
fi
|
|
log "${BLUE}[Info] Running meson test...${NC}"
|
|
if ! meson test -C "${BUILD_DIR}"; then
|
|
log "${RED}[FATAL] Meson tests failed. See log for details.${NC}"; return 1;
|
|
fi
|
|
log "${GREEN}[Success] Tests passed.${NC}"
|
|
}
|
|
|
|
# --- TUI Functions ---
|
|
|
|
check_dialog_installed() {
|
|
if ! check_command dialog; then
|
|
log "${YELLOW}[Warn] The 'dialog' utility is required for TUI mode.${NC}"
|
|
local install_cmd
|
|
install_cmd=$(get_install_cmd "dialog")
|
|
if [ -n "$install_cmd" ]; then
|
|
if prompt_yes_no "Attempt to install it now? (y/n):"; then
|
|
eval "$install_cmd"
|
|
if ! check_command dialog; then
|
|
log "${RED}[FATAL] Failed to install 'dialog'. Cannot run in TUI mode.${NC}"
|
|
return 1
|
|
fi
|
|
else
|
|
log "${RED}[FATAL] Cannot run in TUI mode without 'dialog'. Exiting.${NC}"
|
|
return 1
|
|
fi
|
|
else
|
|
log "${RED}[FATAL] Cannot auto-install 'dialog'. Please install it manually.${NC}"
|
|
return 1
|
|
fi
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
run_dependency_installer_tui() {
|
|
# This function now just calls the check functions to populate status
|
|
declare -A DEP_STATUS
|
|
check_compiler >/dev/null; DEP_STATUS[compiler]=$?
|
|
check_python_dev >/dev/null; DEP_STATUS[python-dev]=$?
|
|
check_meson_python >/dev/null; DEP_STATUS[meson-python]=$?
|
|
check_cmake >/dev/null; DEP_STATUS[cmake]=$?
|
|
check_meson >/dev/null; DEP_STATUS[meson]=$?
|
|
if [[ $BOOST_OKAY = false ]]; then
|
|
DEP_STATUS[boost]=1 # Force boost to be "not okay" if previous check failed
|
|
fi
|
|
|
|
local choices
|
|
choices=$(dialog --clear --backtitle "Project Dependency Installer" \
|
|
--title "Install System Dependencies" \
|
|
--checklist "Select dependencies to install. Already found dependencies are unchecked." 20 70 6 \
|
|
"compiler" "C++ Compilers (g++, clang++)" "$([[ ${DEP_STATUS[compiler]} -ne 0 ]] && echo "on" || echo "off")" \
|
|
"python-dev" "Python 3 Dev Headers" "$([[ ${DEP_STATUS[python-dev]} -ne 0 ]] && echo "on" || echo "off")" \
|
|
"meson-python" "meson-python (for Python bindings)" "$([[ ${DEP_STATUS[meson-python]} -ne 0 ]] && echo "on" || echo "off")" \
|
|
"cmake" "CMake" "$([[ ${DEP_STATUS[cmake]} -ne 0 ]] && echo "on" || echo "off")" \
|
|
"meson" "Meson Build System (>=${MIN_MESON_VER})" "$([[ ${DEP_STATUS[meson]} -ne 0 ]] && echo "on" || echo "off")" \
|
|
"boost" "Boost Libraries (system package)" "$([[ ${DEP_STATUS[boost]} -ne 0 ]] && echo "on" || echo "off")" \
|
|
3>&1 1>&2 2>&3)
|
|
|
|
clear
|
|
if [ -z "$choices" ]; then log "${YELLOW}[Info] No dependencies selected.${NC}"; return; fi
|
|
|
|
for choice in $choices; do
|
|
local dep; dep=$(echo "$choice" | tr -d '"')
|
|
log "\n${BLUE}--- Installing ${dep} ---${NC}"
|
|
local install_cmd; install_cmd=$(get_install_cmd "$dep")
|
|
if [ -n "$install_cmd" ]; then
|
|
eval "$install_cmd" 2>&1 | tee -a "$LOGFILE"
|
|
else
|
|
log "${RED}[Error] No automatic installation command for '${dep}'. Please install manually.${NC}"
|
|
dialog --msgbox "Could not find an automatic installation command for '${dep}' on your system. Please install it manually." 8 60
|
|
fi
|
|
done
|
|
# Re-run check to update status
|
|
check_compiler
|
|
}
|
|
|
|
run_python_bindings_tui() {
|
|
if ! check_meson_python; then
|
|
dialog --msgbox "The 'meson-python' package is required to build Python bindings. Please install it from the 'Install System Dependencies' menu first." 10 70
|
|
return
|
|
fi
|
|
if [ -z "$CC_COMPILER" ]; then
|
|
dialog --msgbox "No C++ compiler is selected. Please configure one from the 'Configure Build Options' menu before building the Python bindings." 10 70
|
|
return
|
|
fi
|
|
|
|
local python_exec
|
|
python_exec=$(command -v python3)
|
|
if [ -z "$python_exec" ]; then
|
|
dialog --msgbox "Could not find 'python3' executable. Please ensure Python 3 is installed and in your PATH." 8 60
|
|
return
|
|
fi
|
|
|
|
local choice
|
|
choice=$(dialog --clear --backtitle "Python Bindings Installer" \
|
|
--title "Install Python Bindings" \
|
|
--menu "Using Python: ${python_exec}\nUsing C++ Compiler: ${CC_COMPILER}\n\nSelect installation mode:" 15 70 2 \
|
|
"1" "Developer Mode (pip install -e .)" \
|
|
"2" "User Mode (pip install .)" \
|
|
3>&1 1>&2 2>&3)
|
|
|
|
clear
|
|
case "$choice" in
|
|
1)
|
|
log "${BLUE}[Info] Installing Python bindings in Developer Mode...${NC}"
|
|
if ! CC="${C_COMPILER}" CXX="${CC_COMPILER}" FC="${FC_COMPILER}" pip install -e . --no-build-isolation -vv; then
|
|
log "${RED}[Error] Failed to install Python bindings in developer mode.${NC}"
|
|
dialog --msgbox "Developer mode installation failed. Check the log for details." 8 60
|
|
else
|
|
log "${GREEN}[Success] Python bindings installed in developer mode.${NC}"
|
|
dialog --msgbox "Successfully installed Python bindings in developer mode." 8 60
|
|
fi
|
|
;;
|
|
2)
|
|
log "${BLUE}[Info] Installing Python bindings in User Mode...${NC}"
|
|
if ! CC="${C_COMPILER}" CXX="${CC_COMPILER}" FC="${FC_COMPILER}" pip install .; then
|
|
log "${RED}[Error] Failed to install Python bindings in user mode.${NC}"
|
|
dialog --msgbox "User mode installation failed. Check the log for details." 8 60
|
|
else
|
|
log "${GREEN}[Success] Python bindings installed in user mode.${NC}"
|
|
dialog --msgbox "Successfully installed Python bindings in user mode." 8 60
|
|
fi
|
|
;;
|
|
*)
|
|
log "${YELLOW}[Info] Python binding installation cancelled.${NC}"
|
|
;;
|
|
esac
|
|
}
|
|
|
|
run_compiler_help_tui() {
|
|
local compiler_name="$1"
|
|
local req_ver=""
|
|
local help_text=""
|
|
|
|
if [[ "$compiler_name" == "g++" ]]; then
|
|
req_ver=$MIN_GCC_VER
|
|
help_text="The installer could not automatically install GCC >= ${req_ver}.\n\n"
|
|
help_text+="This can happen on older Linux distributions.\n\n"
|
|
help_text+="Recommended Solutions:\n"
|
|
help_text+="1. (Ubuntu/Debian) Add a PPA with newer compilers:\n"
|
|
help_text+=" sudo add-apt-repository ppa:ubuntu-toolchain-r/test\n"
|
|
help_text+=" sudo apt-get update\n"
|
|
help_text+=" sudo apt-get install g++-13\n\n"
|
|
help_text+="2. Download pre-built binaries from a trusted source.\n\n"
|
|
help_text+="3. Build GCC from source (advanced)."
|
|
|
|
elif [[ "$compiler_name" == "clang++" ]]; then
|
|
req_ver=$MIN_CLANG_VER
|
|
help_text="The installer could not automatically install Clang >= ${req_ver}.\n\n"
|
|
help_text+="This is common on systems like Ubuntu 22.04.\n\n"
|
|
help_text+="Recommended Solution (Ubuntu/Debian):\n"
|
|
help_text+="Use the official LLVM APT repository. Run this command in your terminal:\n\n"
|
|
help_text+=' bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)"'
|
|
help_text+="\n\nThis script will set up the repository and install the latest version of Clang. After running it, re-run this installer."
|
|
fi
|
|
|
|
dialog --title "Compiler Installation Failed" --msgbox "$help_text" 25 78
|
|
}
|
|
|
|
|
|
run_compiler_selection_tui() {
|
|
local gpp_ver; gpp_ver=$(g++ -dumpversion 2>/dev/null | grep -oE '[0-9]+(\.[0-9]+)*' | head -n1)
|
|
local clang_ver; clang_ver=$(clang++ --version 2>/dev/null | head -n1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -n1)
|
|
|
|
local gpp_ok=false
|
|
local clang_ok=false
|
|
if [[ -n "$gpp_ver" ]]; then vercomp "$gpp_ver" "$MIN_GCC_VER"; [[ $? -ne 2 ]] && gpp_ok=true; fi
|
|
if [[ -n "$clang_ver" ]]; then vercomp "$clang_ver" "$MIN_CLANG_VER"; [[ $? -ne 2 ]] && clang_ok=true; fi
|
|
|
|
if ! $gpp_ok && ! $clang_ok; then
|
|
# No valid compilers found
|
|
local choices
|
|
choices=$(dialog --title "Compiler Installation" --checklist "No valid C++ compiler found. Please select which to install:" 15 70 2 \
|
|
"g++" "GNU C++ Compiler (>=${MIN_GCC_VER})" "on" \
|
|
"clang++" "Clang C++ Compiler (>=${MIN_CLANG_VER}, often faster)" "off" 3>&1 1>&2 2>&3)
|
|
if [ -n "$choices" ]; then
|
|
for choice in $choices; do
|
|
local compiler_to_install; compiler_to_install=$(echo "$choice" | tr -d '"')
|
|
local install_cmd; install_cmd=$(get_compiler_install_cmd "$compiler_to_install")
|
|
if [ -n "$install_cmd" ]; then
|
|
eval "$install_cmd" 2>&1 | tee -a "$LOGFILE"
|
|
# Post-install check
|
|
check_compiler
|
|
if ! check_command "${VALID_COMPILERS[g++*]}" && [[ "$compiler_to_install" == "g++" ]]; then run_compiler_help_tui "g++"; fi
|
|
if ! check_command "${VALID_COMPILERS[clang++*]}" && [[ "$compiler_to_install" == "clang++" ]]; then run_compiler_help_tui "clang++"; fi
|
|
fi
|
|
done
|
|
fi
|
|
elif ! $gpp_ok && [[ -n "$gpp_ver" ]]; then
|
|
# g++ found but too old
|
|
if dialog --title "Compiler Update" --yesno "Found g++ version ${gpp_ver}, but require >= ${MIN_GCC_VER}.\n\nAttempt to install a compatible version?" 10 70; then
|
|
local install_cmd; install_cmd=$(get_compiler_install_cmd "g++")
|
|
if [ -n "$install_cmd" ]; then
|
|
eval "$install_cmd" 2>&1 | tee -a "$LOGFILE"
|
|
check_compiler
|
|
if ! $gpp_ok; then run_compiler_help_tui "g++"; fi
|
|
fi
|
|
fi
|
|
elif ! $clang_ok && [[ -n "$clang_ver" ]]; then
|
|
# clang++ found but too old
|
|
if dialog --title "Compiler Update" --yesno "Found clang++ version ${clang_ver}, but require >= ${MIN_CLANG_VER}.\n\nAttempt to install a compatible version?" 10 70; then
|
|
local install_cmd; install_cmd=$(get_compiler_install_cmd "clang++")
|
|
if [ -n "$install_cmd" ]; then
|
|
eval "$install_cmd" 2>&1 | tee -a "$LOGFILE"
|
|
check_compiler
|
|
if ! $clang_ok; then run_compiler_help_tui "clang++"; fi
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# Re-check compilers and let user choose if multiple are available
|
|
check_compiler
|
|
if [ ${#VALID_COMPILERS[@]} -gt 0 ]; then
|
|
local menu_items=()
|
|
for name in "${!VALID_COMPILERS[@]}"; do
|
|
menu_items+=("$name" "")
|
|
done
|
|
local compiler_choice_key
|
|
compiler_choice_key=$(dialog --title "Select C++ Compiler" --menu "Select the C++ compiler to use:" 15 70 ${#VALID_COMPILERS[@]} "${menu_items[@]}" 3>&1 1>&2 2>&3)
|
|
if [ -n "$compiler_choice_key" ]; then
|
|
CC_COMPILER="${VALID_COMPILERS[$compiler_choice_key]}"
|
|
set_compilers
|
|
log "${BLUE}[Config] Set C compiler to: ${C_COMPILER}${NC}"
|
|
log "${BLUE}[Config] Set C++ compiler to: ${CC_COMPILER}${NC}"
|
|
log "${BLUE}[Config] Set Fortran compiler to: ${FC_COMPILER}${NC}"
|
|
fi
|
|
else
|
|
dialog --msgbox "No valid C++ compiler could be found or installed. Please install one manually that meets the version requirements." 8 70
|
|
fi
|
|
}
|
|
|
|
run_advanced_options_tui() {
|
|
while true; do
|
|
local choice
|
|
choice=$(dialog --clear --backtitle "Advanced Build Options" \
|
|
--title "Advanced Meson Core & Base Options" \
|
|
--menu "Select an option to configure:" 20 78 10 \
|
|
"1" "Backend (current: ${MESON_ADVANCED_OPTS[backend]})" \
|
|
"2" "Default Library (current: ${MESON_ADVANCED_OPTS[default_library]})" \
|
|
"3" "LTO (Link-Time Opt) (current: ${MESON_ADVANCED_OPTS[b_lto]})" \
|
|
"4" "PCH (Precompiled H) (current: ${MESON_ADVANCED_OPTS[b_pch]})" \
|
|
"5" "Coverage (current: ${MESON_ADVANCED_OPTS[b_coverage]})" \
|
|
"6" "Strip on install (current: ${MESON_ADVANCED_OPTS[strip]})" \
|
|
"7" "Unity build (current: ${MESON_ADVANCED_OPTS[unity]})" \
|
|
"8" "Warning Level (current: ${MESON_ADVANCED_OPTS[warning_level]})" \
|
|
"9" "Warnings as Errors (current: ${MESON_ADVANCED_OPTS[werror]})" \
|
|
"10" "Back to main config" \
|
|
3>&1 1>&2 2>&3)
|
|
|
|
case "$choice" in
|
|
1)
|
|
local backend_choice
|
|
backend_choice=$(dialog --title "Select Backend" --menu "" 15 70 4 \
|
|
"ninja" "Ninja build system" \
|
|
"vs" "Visual Studio" \
|
|
"xcode" "Xcode" \
|
|
"none" "No backend" 3>&1 1>&2 2>&3)
|
|
if [ -n "$backend_choice" ]; then MESON_ADVANCED_OPTS["backend"]="$backend_choice"; fi
|
|
;;
|
|
2)
|
|
local lib_choice
|
|
lib_choice=$(dialog --title "Default Library Type" --menu "" 15 70 3 \
|
|
"shared" "Shared (.so, .dylib)" \
|
|
"static" "Static (.a)" \
|
|
"both" "Build both types" 3>&1 1>&2 2>&3)
|
|
if [ -n "$lib_choice" ]; then MESON_ADVANCED_OPTS["default_library"]="$lib_choice"; fi
|
|
;;
|
|
3)
|
|
if dialog --title "LTO (Link-Time Optimization)" --yesno "Enable Link-Time Optimization?\n(Can improve performance but increases link time)" 8 70; then
|
|
MESON_ADVANCED_OPTS["b_lto"]="true"
|
|
else
|
|
MESON_ADVANCED_OPTS["b_lto"]="false"
|
|
fi
|
|
;;
|
|
4)
|
|
if dialog --title "PCH (Precompiled Headers)" --yesno "Use precompiled headers?\n(Can speed up compilation)" 8 70; then
|
|
MESON_ADVANCED_OPTS["b_pch"]="true"
|
|
else
|
|
MESON_ADVANCED_OPTS["b_pch"]="false"
|
|
fi
|
|
;;
|
|
5)
|
|
if dialog --title "Enable Coverage Tracking" --yesno "Enable code coverage tracking?\n(For generating coverage reports)" 8 70; then
|
|
MESON_ADVANCED_OPTS["b_coverage"]="true"
|
|
else
|
|
MESON_ADVANCED_OPTS["b_coverage"]="false"
|
|
fi
|
|
;;
|
|
6)
|
|
if dialog --title "Strip on Install" --yesno "Strip binaries on install?\n(Reduces size, removes debug symbols)" 8 70; then
|
|
MESON_ADVANCED_OPTS["strip"]="true"
|
|
else
|
|
MESON_ADVANCED_OPTS["strip"]="false"
|
|
fi
|
|
;;
|
|
7)
|
|
local unity_choice
|
|
unity_choice=$(dialog --title "Unity Build" --menu "" 15 70 3 \
|
|
"on" "Enable for all targets" \
|
|
"subprojects" "Enable for subprojects only" \
|
|
"off" "Disable unity builds" 3>&1 1>&2 2>&3)
|
|
if [ -n "$unity_choice" ]; then MESON_ADVANCED_OPTS["unity"]="$unity_choice"; fi
|
|
;;
|
|
8)
|
|
local warn_choice
|
|
warn_choice=$(dialog --title "Compiler Warning Level" --menu "" 15 70 5 \
|
|
"0" "No warnings" \
|
|
"1" "Normal warnings" \
|
|
"2" "More warnings" \
|
|
"3" "All warnings" \
|
|
"everything" "Pedantic warnings" 3>&1 1>&2 2>&3)
|
|
if [ -n "$warn_choice" ]; then MESON_ADVANCED_OPTS["warning_level"]="$warn_choice"; fi
|
|
;;
|
|
9)
|
|
if dialog --title "Treat Warnings as Errors" --yesno "Enable -Werror?\n(Build will fail on any compiler warning)" 8 70; then
|
|
MESON_ADVANCED_OPTS["werror"]="true"
|
|
else
|
|
MESON_ADVANCED_OPTS["werror"]="false"
|
|
fi
|
|
;;
|
|
10) break ;;
|
|
*) break ;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
|
|
run_build_config_tui() {
|
|
local choice
|
|
choice=$(dialog --clear --backtitle "Build Configuration" \
|
|
--title "Configure Build Options" \
|
|
--menu "Select an option to configure:" 20 70 8 \
|
|
"1" "Build Directory (current: ${BUILD_DIR})" \
|
|
"2" "Install Prefix (current: ${INSTALL_PREFIX})" \
|
|
"3" "Manage & Select C/C++/Fortran Compiler" \
|
|
"4" "Build Type (current: ${MESON_BUILD_TYPE})" \
|
|
"5" "Log Level (current: ${MESON_LOG_LEVEL})" \
|
|
"6" "Generate pkg-config (current: ${MESON_PKG_CONFIG})" \
|
|
"7" "Number of Cores (current: ${MESON_NUM_CORES})" \
|
|
"8" "Advanced Build Options" \
|
|
3>&1 1>&2 2>&3)
|
|
|
|
clear
|
|
case "$choice" in
|
|
1)
|
|
local new_dir
|
|
new_dir=$(dialog --title "Set Build Directory" --inputbox "Enter new build directory name:" 10 60 "${BUILD_DIR}" 3>&1 1>&2 2>&3)
|
|
if [ -n "$new_dir" ]; then
|
|
BUILD_DIR="$new_dir"
|
|
log "${BLUE}[Config] Set build directory to: ${BUILD_DIR}${NC}"
|
|
fi
|
|
;;
|
|
2)
|
|
local new_prefix
|
|
new_prefix=$(dialog --title "Set Install Prefix" --inputbox "Enter absolute path for installation prefix:" 10 60 "${INSTALL_PREFIX}" 3>&1 1>&2 2>&3)
|
|
if [ -n "$new_prefix" ]; then
|
|
INSTALL_PREFIX="$new_prefix"
|
|
log "${BLUE}[Config] Set install prefix to: ${INSTALL_PREFIX}${NC}"
|
|
fi
|
|
;;
|
|
3)
|
|
run_compiler_selection_tui
|
|
;;
|
|
4)
|
|
local build_type_choice
|
|
build_type_choice=$(dialog --title "Select Build Type" --menu "" 15 70 4 \
|
|
"debug" "No optimizations, with debug symbols" \
|
|
"release" "Optimized for performance" \
|
|
"debugoptimized" "With debug symbols and optimization" \
|
|
"plain" "Custom flags only" \
|
|
3>&1 1>&2 2>&3)
|
|
if [ -n "$build_type_choice" ]; then
|
|
MESON_BUILD_TYPE="$build_type_choice"
|
|
log "${BLUE}[Config] Set build type to: ${MESON_BUILD_TYPE}${NC}"
|
|
fi
|
|
;;
|
|
5)
|
|
local log_level_choice
|
|
log_level_choice=$(dialog --title "Select Log Level" --menu "" 15 70 8 \
|
|
"traceL3" "" "traceL2" "" "traceL1" "" "debug" "" "info" "" "warning" "" "error" "" "critical" "" \
|
|
3>&1 1>&2 2>&3)
|
|
if [ -n "$log_level_choice" ]; then
|
|
MESON_LOG_LEVEL="$log_level_choice"
|
|
log "${BLUE}[Config] Set log level to: ${MESON_LOG_LEVEL}${NC}"
|
|
fi
|
|
;;
|
|
6)
|
|
if dialog --title "Generate pkg-config" --yesno "Generate gridfire.pc file?" 7 60; then
|
|
MESON_PKG_CONFIG="true"
|
|
else
|
|
MESON_PKG_CONFIG="false"
|
|
fi
|
|
log "${BLUE}[Config] Set pkg-config generation to: ${MESON_PKG_CONFIG}${NC}"
|
|
;;
|
|
7)
|
|
local max_cores; max_cores=$(getconf _NPROCESSORS_ONLN 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 1)
|
|
local core_choice
|
|
core_choice=$(dialog --title "Set Number of Cores" --inputbox "Enter number of cores for compilation.\nAvailable: ${max_cores}" 10 60 "${MESON_NUM_CORES}" 3>&1 1>&2 2>&3)
|
|
if [[ "$core_choice" =~ ^[0-9]+$ ]] && [ "$core_choice" -gt 0 ]; then
|
|
MESON_NUM_CORES="$core_choice"
|
|
log "${BLUE}[Config] Set number of cores to: ${MESON_NUM_CORES}${NC}"
|
|
elif [ -n "$core_choice" ]; then
|
|
dialog --msgbox "Invalid input. Please enter a positive number." 6 40
|
|
fi
|
|
;;
|
|
8)
|
|
run_advanced_options_tui
|
|
;;
|
|
esac
|
|
}
|
|
|
|
run_notes_tui() {
|
|
if [ ! -f "$NOTES_FILE" ]; then
|
|
dialog --msgbox "Notes file '${NOTES_FILE}' not found." 8 50
|
|
return
|
|
fi
|
|
|
|
local notes_content=""
|
|
local counter=1
|
|
# Read file, filter comments, and process non-empty lines
|
|
while IFS= read -r line; do
|
|
# Skip empty or comment lines
|
|
if [[ -z "$line" || "$line" =~ ^[[:space:]]*# ]]; then
|
|
continue
|
|
fi
|
|
notes_content+="${counter}. ${line}\n\n"
|
|
((counter++))
|
|
done < "$NOTES_FILE"
|
|
|
|
if [ -z "$notes_content" ]; then
|
|
dialog --msgbox "No notes found in '${NOTES_FILE}'." 8 50
|
|
else
|
|
dialog --title "Installer Notes" --msgbox "$notes_content" 20 70
|
|
fi
|
|
}
|
|
|
|
run_save_config_tui() {
|
|
local file_to_save
|
|
file_to_save=$(dialog --title "Save Configuration" --inputbox "Enter filename to save configuration:" 10 60 "$CONFIG_FILE" 3>&1 1>&2 2>&3)
|
|
if [ -z "$file_to_save" ]; then
|
|
dialog --msgbox "Save cancelled." 8 50
|
|
return
|
|
fi
|
|
|
|
# Write main settings
|
|
{
|
|
echo "# GridFire Build Configuration"
|
|
echo "BUILD_DIR=\"$BUILD_DIR\""
|
|
echo "INSTALL_PREFIX=\"$INSTALL_PREFIX\""
|
|
echo "MESON_BUILD_TYPE=\"$MESON_BUILD_TYPE\""
|
|
echo "MESON_LOG_LEVEL=\"$MESON_LOG_LEVEL\""
|
|
echo "MESON_PKG_CONFIG=\"$MESON_PKG_CONFIG\""
|
|
echo "MESON_NUM_CORES=\"$MESON_NUM_CORES\""
|
|
echo "C_COMPILER=\"$C_COMPILER\""
|
|
echo "CC_COMPILER=\"$CC_COMPILER\""
|
|
echo "FC_COMPILER=\"$FC_COMPILER\""
|
|
echo ""
|
|
echo "# Advanced Meson Options"
|
|
for key in "${!MESON_ADVANCED_OPTS[@]}"; do
|
|
echo "MESON_ADVANCED_OPTS[\"$key\"]=\"${MESON_ADVANCED_OPTS[$key]}\""
|
|
done
|
|
} > "$file_to_save"
|
|
|
|
dialog --title "Success" --msgbox "Configuration saved to:\n${file_to_save}" 8 60
|
|
}
|
|
|
|
run_load_config_tui() {
|
|
local file_to_load
|
|
file_to_load=$(dialog --title "Load Configuration" --inputbox "Enter filename to load configuration:" 10 60 "$CONFIG_FILE" 3>&1 1>&2 2>&3)
|
|
if [ -z "$file_to_load" ]; then
|
|
dialog --msgbox "Load cancelled." 8 50
|
|
return
|
|
fi
|
|
|
|
if [ ! -f "$file_to_load" ]; then
|
|
dialog --msgbox "Error: File not found:\n${file_to_load}" 8 60
|
|
return
|
|
fi
|
|
|
|
# Source the file to load the variables
|
|
# We need to re-declare the associative array for it to be populated by source
|
|
declare -A MESON_ADVANCED_OPTS
|
|
# shellcheck source=/dev/null
|
|
. "$file_to_load"
|
|
|
|
# Re-validate the compiler
|
|
check_compiler
|
|
if ! check_command "$CC_COMPILER"; then
|
|
dialog --title "Warning" --msgbox "Compiler '${CC_COMPILER}' from config file not found.\nRe-detecting a valid compiler." 10 60
|
|
check_compiler
|
|
fi
|
|
|
|
dialog --title "Success" --msgbox "Configuration loaded from:\n${file_to_load}" 8 60
|
|
}
|
|
|
|
run_boost_help_tui() {
|
|
local help_text="The version of the Boost library found on your system is not compatible with the selected C++ compiler and the C++23 standard. This is a common issue on distributions like Ubuntu that may have older versions of Boost in their package repositories.\n\n"
|
|
help_text+="To resolve this, you need to manually download, build, and install a newer version of Boost (e.g., 1.83.0 or newer).\n\n"
|
|
help_text+="Recommended Steps:\n"
|
|
help_text+="1. Download the latest Boost source from boost.org.\n"
|
|
help_text+="2. Follow their instructions to build it. A typical sequence is:\n"
|
|
help_text+=" ./bootstrap.sh\n"
|
|
help_text+=" ./b2 install\n"
|
|
help_text+="3. Take note of the installation prefix (e.g., /usr/local).\n"
|
|
help_text+="4. Set the BOOST_ROOT environment variable before running this script again:\n"
|
|
help_text+=" export BOOST_ROOT=/path/to/your/boost/install\n"
|
|
help_text+=" ./install.sh --tui\n\n"
|
|
help_text+="Meson should then automatically find and use your manually installed version."
|
|
|
|
dialog --title "Boost Compatibility Issue" --msgbox "$help_text" 25 78
|
|
}
|
|
|
|
|
|
run_main_tui() {
|
|
if ! check_dialog_installed; then return 1; fi
|
|
# Initial check to populate compiler list and set a default
|
|
check_compiler
|
|
|
|
local sudo_status="User Mode"
|
|
if [ "$EUID" -eq 0 ]; then
|
|
sudo_status="Root/Sudo Mode"
|
|
fi
|
|
|
|
while true; do
|
|
# Re-check boost status to update menu dynamically
|
|
if [[ $BOOST_CHECKED = false ]]; then
|
|
log "${BLUE}[Info] Checking Boost library status (this may take a minute)...${NC}"
|
|
# If BOOST_CHECKED is set, we assume Boost was checked previously
|
|
check_boost >/dev/null 2>&1 || BOOST_OKAY=false
|
|
BOOST_CHECKED=true
|
|
fi
|
|
|
|
local menu_items=(
|
|
"1" "Install System Dependencies"
|
|
"2" "Configure Build Options"
|
|
"3" "Install Python Bindings"
|
|
)
|
|
if $BOOST_OKAY; then
|
|
menu_items+=(
|
|
"4" "Run Full Build (Setup + Compile)"
|
|
"5" "Run Meson Setup/Reconfigure"
|
|
"6" "Run Meson Compile"
|
|
"7" "Run Meson Install (requires sudo)"
|
|
"8" "Run Tests"
|
|
"N" "View Notes"
|
|
"S" "Save Configuration"
|
|
"L" "Load Configuration"
|
|
)
|
|
fi
|
|
if ! $BOOST_OKAY; then
|
|
menu_items+=("B" "Boost Error Detected! Help with Boost Issues")
|
|
fi
|
|
menu_items+=("Q" "Exit")
|
|
|
|
|
|
local choice
|
|
choice=$(dialog --clear --backtitle "GridFire Installer - [${sudo_status}]" \
|
|
--title "Main Menu" \
|
|
--menu "C: ${C_COMPILER:-N/A} C++: ${CC_COMPILER:-N/A} FC: ${FC_COMPILER:-N/A}\nDIR: ${BUILD_DIR} | TYPE: ${MESON_BUILD_TYPE} | CORES: ${MESON_NUM_CORES}\nPREFIX: ${INSTALL_PREFIX}\nLOG: ${MESON_LOG_LEVEL} | PKG-CONFIG: ${MESON_PKG_CONFIG}" 24 78 14 \
|
|
"${menu_items[@]}" \
|
|
3>&1 1>&2 2>&3)
|
|
|
|
clear
|
|
case "$choice" in
|
|
1) run_dependency_installer_tui ;;
|
|
2) run_build_config_tui ;;
|
|
3) run_python_bindings_tui ;;
|
|
4) run_meson_setup && run_meson_compile ;;
|
|
5) run_meson_setup ;;
|
|
6) run_meson_compile ;;
|
|
7) run_meson_install ;;
|
|
8) run_meson_tests ;;
|
|
N) run_notes_tui ;;
|
|
S) run_save_config_tui ;;
|
|
L) run_load_config_tui ;;
|
|
B) run_boost_help_tui ;;
|
|
Q) break ;;
|
|
*) log "${YELLOW}[Info] TUI cancelled.${NC}"; break ;;
|
|
esac
|
|
done
|
|
clear
|
|
}
|
|
|
|
# --- Script Entry Point ---
|
|
main() {
|
|
if [[ " $@ " =~ " --help " ]] || [[ " $@ " =~ " -h " ]]; then show_help; exit 0; fi
|
|
if [[ " $@ " =~ " --clean " ]]; then log "${BLUE}[Info] Cleaning up...${NC}"; rm -rf "$BUILD_DIR" "$LOGFILE"; fi
|
|
|
|
local useTUI=0
|
|
if [[ " $@ " =~ " --tui " ]]; then
|
|
useTUI=1
|
|
log "${BLUE}[Info] Using TUI mode...${NC}"
|
|
fi
|
|
|
|
# Handle --config argument
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
--config)
|
|
if [ -f "$2" ]; then
|
|
log "${BLUE}[Info] Loading configuration from $2...${NC}"
|
|
declare -A MESON_ADVANCED_OPTS
|
|
# shellcheck source=/dev/null
|
|
. "$2"
|
|
else
|
|
log "${RED}[Error] Configuration file not found: $2${NC}"
|
|
exit 1
|
|
fi
|
|
shift 2
|
|
;;
|
|
*)
|
|
shift
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [[ $useTUI -eq 1 ]]; then
|
|
run_main_tui
|
|
log "${GREEN}Exited TUI mode.${NC}"
|
|
exit 0
|
|
fi
|
|
|
|
|
|
echo "" > "$LOGFILE" # Clear log file
|
|
log "--- GridFire Installation Log ---"
|
|
log "Date: $(date)"
|
|
log "OS: ${OS_NAME}, Distro: ${DISTRO_ID}"
|
|
|
|
|
|
|
|
|
|
# --- Non-TUI path ---
|
|
log "\n${BLUE}--- Checking System Dependencies (CLI Mode) ---${NC}"
|
|
# Run check_compiler first to set the default
|
|
check_compiler
|
|
declare -A CHECKS=(
|
|
[python-dev]="check_python_dev" [meson-python]="check_meson_python" [cmake]="check_cmake"
|
|
[meson]="check_meson" [boost]="check_boost"
|
|
)
|
|
if ! check_compiler; then
|
|
# Handle case where no compiler is found
|
|
local install_cmd; install_cmd=$(get_install_cmd "compiler")
|
|
if [ -n "$install_cmd" ]; then
|
|
if prompt_yes_no "Dependency 'compiler' is missing. Attempt to install? (y/n):"; then
|
|
eval "$install_cmd" 2>&1 | tee -a "$LOGFILE"
|
|
fi
|
|
else
|
|
log "${RED}[Error] No automatic installation for 'compiler'. Please install manually.${NC}"
|
|
fi
|
|
fi
|
|
|
|
for dep in "${!CHECKS[@]}"; do
|
|
if ! ${CHECKS[$dep]}; then
|
|
local install_cmd; install_cmd=$(get_install_cmd "$dep")
|
|
if [ -n "$install_cmd" ]; then
|
|
if prompt_yes_no "Dependency '${dep}' is missing. Attempt to install? (y/n):"; then
|
|
eval "$install_cmd" 2>&1 | tee -a "$LOGFILE"
|
|
fi
|
|
else
|
|
log "${RED}[Error] No automatic installation for '${dep}'. Please install manually.${NC}"
|
|
fi
|
|
fi
|
|
done
|
|
|
|
log "\n${BLUE}--- Re-checking all dependencies ---${NC}"
|
|
local final_fail=false
|
|
# Re-add compiler check to the list for final verification
|
|
CHECKS[compiler]="check_compiler"
|
|
for dep in "${!CHECKS[@]}"; do
|
|
if ! ${CHECKS[$dep]}; then
|
|
log "${RED}[FATAL] Dependency still missing: ${dep}${NC}"
|
|
final_fail=true
|
|
fi
|
|
done
|
|
|
|
if $final_fail; then
|
|
log "${RED}Please install missing dependencies and re-run.${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
log "\n${GREEN}--- All dependencies met. Proceeding with build. ---${NC}"
|
|
run_meson_setup && run_meson_compile
|
|
}
|
|
|
|
main "$@"
|