2025-07-29 09:05:47 -04:00
#!/usr/bin/env bash
#
2025-07-29 09:25:44 -04:00
# install.sh - Comprehensive interactive installation script for GridFire.
2025-07-29 09:05:47 -04:00
#
# This script performs the following actions:
2025-07-29 10:54:49 -04:00
# 1. Checks for essential system dependencies against required minimum versions.
2025-07-29 09:25:44 -04:00
# 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.
2025-07-29 09:05:47 -04:00
# 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.
2025-07-29 09:25:44 -04:00
# 5. Once all dependencies are met, it can run the meson setup, compile, install, and test commands.
2025-07-29 09:05:47 -04:00
# 6. Logs all operations to a file for easy debugging.
set -o pipefail
# --- Configuration ---
LOGFILE = "GridFire_Installer.log"
2025-07-29 10:54:49 -04:00
NOTES_FILE = "notes.txt"
MIN_GCC_VER = "13.0.0"
MIN_CLANG_VER = "16.0.0"
MIN_MESON_VER = "1.5.0"
2025-07-29 09:25:44 -04:00
# --- Build Configuration Globals ---
2025-07-29 09:05:47 -04:00
BUILD_DIR = "build"
2025-07-29 09:25:44 -04:00
INSTALL_PREFIX = "/usr/local"
2025-07-29 10:54:49 -04:00
MESON_BUILD_TYPE = "debug" # Default to debug to match Meson's default and avoid optimization warnings
2025-07-29 09:25:44 -04:00
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)
2025-07-29 10:54:49 -04:00
C_COMPILER = ""
2025-07-29 09:44:29 -04:00
CC_COMPILER = ""
2025-07-29 10:54:49 -04:00
FC_COMPILER = ""
declare -A VALID_COMPILERS
2025-07-29 09:05:47 -04:00
# --- 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 "
}
2025-07-29 10:54:49 -04:00
# 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
2025-07-29 09:05:47 -04:00
}
2025-07-29 10:54:49 -04:00
2025-07-29 09:05:47 -04:00
# 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:"
2025-07-29 09:25:44 -04:00
echo " --tui Run in Text-based User Interface mode for interactive dependency installation and build control."
2025-07-29 09:05:47 -04:00
echo " --help, -h Show this help message and exit."
echo " --clean Remove the build directory and log file before starting."
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 ---
2025-07-29 09:25:44 -04:00
check_command( ) {
command -v " $1 " & >/dev/null
}
2025-07-29 10:54:49 -04:00
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
}
2025-07-29 09:05:47 -04:00
check_compiler( ) {
2025-07-29 10:54:49 -04:00
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
2025-07-29 09:44:29 -04:00
2025-07-29 10:54:49 -04:00
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
2025-07-29 09:44:29 -04:00
fi
2025-07-29 09:05:47 -04:00
}
check_python_dev( ) {
2025-07-29 09:25:44 -04:00
if check_command python3 && python3-config --includes & >/dev/null; then
2025-07-29 09:05:47 -04:00
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
}
2025-07-29 09:44:29 -04:00
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
}
2025-07-29 09:05:47 -04:00
check_cmake( ) {
2025-07-29 09:25:44 -04:00
if check_command cmake; then
2025-07-29 09:05:47 -04:00
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( ) {
2025-07-29 10:54:49 -04:00
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
2025-07-29 09:05:47 -04:00
}
check_boost( ) {
log " ${ BLUE } [Info] Checking for Boost dependency using Meson... ${ NC } "
local test_dir = "meson-boost-test"
2025-07-29 09:25:44 -04:00
rm -rf " $test_dir " && mkdir -p " $test_dir "
2025-07-29 09:05:47 -04:00
cat > " $test_dir /meson.build " <<EOF
project( 'boost-check' , 'cpp' )
boost_dep = dependency( 'boost' , required: true )
EOF
if meson setup " $test_dir /build " " $test_dir " & >/dev/null; then
log " ${ GREEN } [OK] Found Boost libraries. ${ NC } "
rm -rf " $test_dir "
return 0
else
log " ${ RED } [FAIL] Boost libraries not found by Meson. ${ NC } "
rm -rf " $test_dir "
return 1
fi
}
# --- Dependency Installation Functions ---
2025-07-29 09:44:29 -04:00
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 " ; ;
2025-07-29 10:54:49 -04:00
"clang++" ) cmd = " $brew_cmd install llvm " ; ; # llvm provides clang
2025-07-29 09:44:29 -04:00
esac
; ;
"Linux" )
case " $DISTRO_ID " in
"ubuntu" | "debian" | "linuxmint" )
case " $compiler_to_install " in
2025-07-29 10:54:49 -04:00
"g++" ) cmd = "sudo apt-get install -y g++-13 gfortran-13" ; ;
"clang++" ) cmd = "sudo apt-get install -y clang-16" ; ;
2025-07-29 09:44:29 -04:00
esac
; ;
"fedora" )
2025-07-29 10:54:49 -04:00
# Fedora usually has recent versions in main repos
2025-07-29 09:44:29 -04:00
case " $compiler_to_install " in
2025-07-29 10:54:49 -04:00
"g++" ) cmd = "sudo dnf install -y gcc-c++ gcc-gfortran" ; ;
2025-07-29 09:44:29 -04:00
"clang++" ) cmd = "sudo dnf install -y clang" ; ;
esac
; ;
"arch" | "manjaro" )
2025-07-29 10:54:49 -04:00
# Arch is rolling release, should be fine
2025-07-29 09:44:29 -04:00
case " $compiler_to_install " in
2025-07-29 10:54:49 -04:00
"g++" ) cmd = "sudo pacman -S --noconfirm gcc gcc-fortran" ; ;
2025-07-29 09:44:29 -04:00
"clang++" ) cmd = "sudo pacman -S --noconfirm clang" ; ;
esac
; ;
esac
; ;
esac
echo " $cmd "
2025-07-29 09:05:47 -04:00
}
2025-07-29 09:44:29 -04:00
2025-07-29 09:05:47 -04:00
get_install_cmd( ) {
local dep_name = " $1 "
local cmd = ""
case " $OS_NAME " in
"macOS" )
2025-07-29 09:25:44 -04:00
[ ! -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)
2025-07-29 09:05:47 -04:00
case " $dep_name " in
2025-07-29 10:54:49 -04:00
"compiler" ) cmd = " $brew_cmd install gcc llvm " ; ; # Install both
2025-07-29 09:25:44 -04:00
"python-dev" ) cmd = " $brew_cmd install python3 " ; ;
2025-07-29 09:44:29 -04:00
"meson-python" ) cmd = "python3 -m pip install meson-python" ; ;
2025-07-29 10:54:49 -04:00
"meson" ) cmd = "python3 -m pip install --upgrade meson" ; ;
2025-07-29 09:25:44 -04:00
"cmake" ) cmd = " $brew_cmd install cmake " ; ;
"boost" ) cmd = " $brew_cmd install boost " ; ;
"dialog" ) cmd = " $brew_cmd install dialog " ; ;
2025-07-29 09:05:47 -04:00
esac
; ;
"Linux" )
case " $DISTRO_ID " in
"ubuntu" | "debian" | "linuxmint" )
case " $dep_name " in
2025-07-29 10:54:49 -04:00
"compiler" ) cmd = "sudo apt-get install -y g++-13 gfortran-13 clang-16" ; ;
2025-07-29 09:05:47 -04:00
"python-dev" ) cmd = "sudo apt-get install -y python3-dev" ; ;
2025-07-29 09:44:29 -04:00
"meson-python" ) cmd = "python3 -m pip install meson-python" ; ;
2025-07-29 10:54:49 -04:00
"meson" ) cmd = "python3 -m pip install --upgrade meson" ; ;
2025-07-29 09:05:47 -04:00
"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
2025-07-29 10:54:49 -04:00
"compiler" ) cmd = "sudo dnf install -y gcc-c++ gcc-gfortran clang" ; ;
2025-07-29 09:05:47 -04:00
"python-dev" ) cmd = "sudo dnf install -y python3-devel" ; ;
2025-07-29 09:44:29 -04:00
"meson-python" ) cmd = "python3 -m pip install meson-python" ; ;
2025-07-29 10:54:49 -04:00
"meson" ) cmd = "python3 -m pip install --upgrade meson" ; ;
2025-07-29 09:05:47 -04:00
"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
2025-07-29 10:54:49 -04:00
"compiler" ) cmd = "sudo pacman -S --noconfirm gcc gcc-fortran clang" ; ;
2025-07-29 09:05:47 -04:00
"python-dev" ) cmd = "sudo pacman -S --noconfirm python" ; ;
2025-07-29 09:44:29 -04:00
"meson-python" ) cmd = "python3 -m pip install meson-python" ; ;
2025-07-29 10:54:49 -04:00
"meson" ) cmd = "python3 -m pip install --upgrade meson" ; ;
2025-07-29 09:05:47 -04:00
"cmake" ) cmd = "sudo pacman -S --noconfirm cmake" ; ;
"boost" ) cmd = "sudo pacman -S --noconfirm boost" ; ;
"dialog" ) cmd = "sudo pacman -S --noconfirm dialog" ; ;
esac
; ;
2025-07-29 09:25:44 -04:00
*) log " ${ YELLOW } [Warn] Unsupported Linux distribution: ${ DISTRO_ID } . ${ NC } " ; ;
2025-07-29 09:05:47 -04:00
esac
; ;
2025-07-29 09:25:44 -04:00
*) log " ${ YELLOW } [Warn] Unsupported OS: ${ OS_NAME } . ${ NC } " ; ;
2025-07-29 09:05:47 -04:00
esac
echo " $cmd "
}
2025-07-29 09:25:44 -04:00
# --- Build Functions ---
2025-07-29 09:05:47 -04:00
2025-07-29 09:25:44 -04:00
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
2025-07-29 10:54:49 -04:00
if [ -z " $CC_COMPILER " ] || [ -z " $C_COMPILER " ] ; then
log " ${ RED } [FATAL] No valid C/C++ compiler selected. Configure one first. ${ NC } " ; return 1;
2025-07-29 09:44:29 -04:00
fi
2025-07-29 09:25:44 -04:00
local reconfigure_flag = ""
if [ -d " $BUILD_DIR " ] ; then
reconfigure_flag = "--reconfigure"
log " ${ YELLOW } [Info] Existing build directory found. Will reconfigure. ${ NC } "
fi
2025-07-29 09:05:47 -04:00
2025-07-29 09:25:44 -04:00
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 } " )
2025-07-29 09:05:47 -04:00
2025-07-29 10:54:49 -04:00
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 } "
2025-07-29 09:25:44 -04:00
log " ${ BLUE } [Info] Running meson setup with options: ${ meson_opts [*] } ${ NC } "
2025-07-29 10:54:49 -04:00
# 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
2025-07-29 09:25:44 -04:00
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 } "
}
2025-07-29 09:05:47 -04:00
2025-07-29 09:25:44 -04:00
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 } "
2025-07-29 09:05:47 -04:00
}
2025-07-29 09:25:44 -04:00
# --- TUI Functions ---
check_dialog_installed( ) {
if ! check_command dialog; then
log " ${ YELLOW } [Warn] The 'dialog' utility is required for TUI mode. ${ NC } "
2025-07-29 09:05:47 -04:00
local install_cmd
install_cmd = $( get_install_cmd "dialog" )
if [ -n " $install_cmd " ] ; then
2025-07-29 09:25:44 -04:00
if prompt_yes_no "Attempt to install it now? (y/n):" ; then
2025-07-29 09:05:47 -04:00
eval " $install_cmd "
2025-07-29 09:25:44 -04:00
if ! check_command dialog; then
2025-07-29 09:05:47 -04:00
log " ${ RED } [FATAL] Failed to install 'dialog'. Cannot run in TUI mode. ${ NC } "
2025-07-29 09:25:44 -04:00
return 1
2025-07-29 09:05:47 -04:00
fi
else
log " ${ RED } [FATAL] Cannot run in TUI mode without 'dialog'. Exiting. ${ NC } "
2025-07-29 09:25:44 -04:00
return 1
2025-07-29 09:05:47 -04:00
fi
else
2025-07-29 09:25:44 -04:00
log " ${ RED } [FATAL] Cannot auto-install 'dialog'. Please install it manually. ${ NC } "
return 1
2025-07-29 09:05:47 -04:00
fi
fi
2025-07-29 09:25:44 -04:00
return 0
}
run_dependency_installer_tui( ) {
2025-07-29 09:44:29 -04:00
# This function now just calls the check functions to populate status
2025-07-29 09:25:44 -04:00
declare -A DEP_STATUS
2025-07-29 09:44:29 -04:00
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] = $?
check_boost >/dev/null; DEP_STATUS[ boost] = $?
2025-07-29 09:05:47 -04:00
local choices
choices = $( dialog --clear --backtitle "Project Dependency Installer" \
2025-07-29 09:25:44 -04:00
--title "Install System Dependencies" \
2025-07-29 09:44:29 -04:00
--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" ) " \
2025-07-29 09:25:44 -04:00
"python-dev" "Python 3 Dev Headers" " $( [ [ ${ DEP_STATUS [python-dev] } -ne 0 ] ] && echo "on" || echo "off" ) " \
2025-07-29 09:44:29 -04:00
"meson-python" "meson-python (for Python bindings)" " $( [ [ ${ DEP_STATUS [meson-python] } -ne 0 ] ] && echo "on" || echo "off" ) " \
2025-07-29 09:25:44 -04:00
"cmake" "CMake" " $( [ [ ${ DEP_STATUS [cmake] } -ne 0 ] ] && echo "on" || echo "off" ) " \
2025-07-29 10:54:49 -04:00
"meson" " Meson Build System (>= ${ MIN_MESON_VER } ) " " $( [ [ ${ DEP_STATUS [meson] } -ne 0 ] ] && echo "on" || echo "off" ) " \
2025-07-29 09:25:44 -04:00
"boost" "Boost Libraries" " $( [ [ ${ DEP_STATUS [boost] } -ne 0 ] ] && echo "on" || echo "off" ) " \
2025-07-29 09:05:47 -04:00
3>& 1 1>& 2 2>& 3)
clear
2025-07-29 09:25:44 -04:00
if [ -z " $choices " ] ; then log " ${ YELLOW } [Info] No dependencies selected. ${ NC } " ; return ; fi
2025-07-29 09:05:47 -04:00
for choice in $choices ; do
2025-07-29 09:25:44 -04:00
local dep; dep = $( echo " $choice " | tr -d '"' )
2025-07-29 09:05:47 -04:00
log " \n ${ BLUE } --- Installing ${ dep } --- ${ NC } "
2025-07-29 09:25:44 -04:00
local install_cmd; install_cmd = $( get_install_cmd " $dep " )
2025-07-29 09:05:47 -04:00
if [ -n " $install_cmd " ] ; then
2025-07-29 09:25:44 -04:00
eval " $install_cmd " 2>& 1 | tee -a " $LOGFILE "
2025-07-29 09:05:47 -04:00
else
2025-07-29 09:25:44 -04:00
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
2025-07-29 09:05:47 -04:00
fi
done
2025-07-29 09:44:29 -04:00
# Re-run check to update status
check_compiler
2025-07-29 09:05:47 -04:00
}
2025-07-29 09:25:44 -04:00
run_python_bindings_tui( ) {
2025-07-29 09:44:29 -04:00
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
2025-07-29 09:25:44 -04:00
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" \
2025-07-29 09:44:29 -04:00
--menu " Using Python: ${ python_exec } \nUsing C++ Compiler: ${ CC_COMPILER } \n\nSelect installation mode: " 15 70 2 \
2025-07-29 09:25:44 -04:00
"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 } "
2025-07-29 10:54:49 -04:00
if ! CC = " ${ C_COMPILER } " CXX = " ${ CC_COMPILER } " FC = " ${ FC_COMPILER } " pip install -e . --no-build-isolation -vv; then
2025-07-29 09:25:44 -04:00
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 } "
2025-07-29 10:54:49 -04:00
if ! CC = " ${ C_COMPILER } " CXX = " ${ CC_COMPILER } " FC = " ${ FC_COMPILER } " pip install .; then
2025-07-29 09:25:44 -04:00
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
}
2025-07-29 09:44:29 -04:00
run_compiler_selection_tui( ) {
2025-07-29 10:54:49 -04:00
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
2025-07-29 09:44:29 -04:00
2025-07-29 10:54:49 -04:00
if ! $gpp_ok && ! $clang_ok ; then
# No valid compilers found
2025-07-29 09:44:29 -04:00
local choices
2025-07-29 10:54:49 -04:00
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)
2025-07-29 09:44:29 -04:00
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 " )
2025-07-29 10:54:49 -04:00
if [ -n " $install_cmd " ] ; then eval " $install_cmd " 2>& 1 | tee -a " $LOGFILE " ; fi
2025-07-29 09:44:29 -04:00
done
fi
2025-07-29 10:54:49 -04:00
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++" )
2025-07-29 09:44:29 -04:00
if [ -n " $install_cmd " ] ; then eval " $install_cmd " 2>& 1 | tee -a " $LOGFILE " ; fi
fi
2025-07-29 10:54:49 -04:00
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++" )
2025-07-29 09:44:29 -04:00
if [ -n " $install_cmd " ] ; then eval " $install_cmd " 2>& 1 | tee -a " $LOGFILE " ; fi
fi
fi
# Re-check compilers and let user choose if multiple are available
check_compiler
2025-07-29 10:54:49 -04:00
if [ ${# VALID_COMPILERS [@] } -gt 0 ] ; then
2025-07-29 09:44:29 -04:00
local menu_items = ( )
2025-07-29 10:54:49 -04:00
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 } "
2025-07-29 09:44:29 -04:00
log " ${ BLUE } [Config] Set C++ compiler to: ${ CC_COMPILER } ${ NC } "
2025-07-29 10:54:49 -04:00
log " ${ BLUE } [Config] Set Fortran compiler to: ${ FC_COMPILER } ${ NC } "
2025-07-29 09:44:29 -04:00
fi
2025-07-29 10:54:49 -04:00
else
dialog --msgbox "No valid C++ compiler could be found or installed. Please install one manually that meets the version requirements." 8 70
2025-07-29 09:44:29 -04:00
fi
}
2025-07-29 09:25:44 -04:00
run_build_config_tui( ) {
local choice
choice = $( dialog --clear --backtitle "Build Configuration" \
--title "Configure Build Options" \
2025-07-29 09:44:29 -04:00
--menu "Select an option to configure:" 20 70 7 \
2025-07-29 09:25:44 -04:00
"1" " Build Directory (current: ${ BUILD_DIR } ) " \
"2" " Install Prefix (current: ${ INSTALL_PREFIX } ) " \
2025-07-29 10:54:49 -04:00
"3" "Manage & Select C/C++/Fortran Compiler" \
2025-07-29 09:44:29 -04:00
"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 } ) " \
2025-07-29 09:25:44 -04:00
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)
2025-07-29 09:44:29 -04:00
run_compiler_selection_tui
; ;
4)
2025-07-29 09:25:44 -04:00
local build_type_choice
2025-07-29 10:54:49 -04:00
build_type_choice = $( dialog --title "Select Build Type" --menu "" 15 70 4 \
"debug" "No optimizations, with debug symbols" \
2025-07-29 09:25:44 -04:00
"release" "Optimized for performance" \
"debugoptimized" "With debug symbols and optimization" \
2025-07-29 10:54:49 -04:00
"plain" "Custom flags only" \
2025-07-29 09:25:44 -04:00
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
; ;
2025-07-29 09:44:29 -04:00
5)
2025-07-29 09:25:44 -04:00
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
; ;
2025-07-29 09:44:29 -04:00
6)
2025-07-29 09:25:44 -04:00
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 } "
; ;
2025-07-29 09:44:29 -04:00
7)
2025-07-29 09:25:44 -04:00
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
; ;
esac
}
2025-07-29 10:54:49 -04:00
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
echo " $line "
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
}
2025-07-29 09:25:44 -04:00
run_main_tui( ) {
if ! check_dialog_installed; then return 1; fi
2025-07-29 09:44:29 -04:00
# 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
2025-07-29 09:25:44 -04:00
2025-07-29 09:44:29 -04:00
while true; do
2025-07-29 09:25:44 -04:00
local choice
2025-07-29 09:44:29 -04:00
choice = $( dialog --clear --backtitle " GridFire Installer - [ ${ sudo_status } ] " \
2025-07-29 09:25:44 -04:00
--title "Main Menu" \
2025-07-29 10:54:49 -04:00
--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 } " 22 78 11 \
2025-07-29 09:25:44 -04:00
"1" "Install System Dependencies" \
"2" "Configure Build Options" \
"3" "Install Python Bindings" \
"4" "Run Full Build (Setup + Compile)" \
"5" "Run Meson Setup/Reconfigure" \
"6" "Run Meson Compile" \
"7" "Run Meson Install (requires sudo)" \
"8" "Run Tests" \
2025-07-29 10:54:49 -04:00
"9" "View Notes" \
"10" "Exit" \
2025-07-29 09:25:44 -04:00
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 ; ;
2025-07-29 10:54:49 -04:00
9) run_notes_tui ; ;
10) break ; ;
2025-07-29 09:25:44 -04:00
*) log " ${ YELLOW } [Info] TUI cancelled. ${ NC } " ; break ; ;
esac
done
clear
}
2025-07-29 09:05:47 -04:00
# --- Script Entry Point ---
main( ) {
2025-07-29 09:25:44 -04:00
if [ [ " $@ " = ~ " --help " ] ] || [ [ " $@ " = ~ " -h " ] ] ; then show_help; exit 0; fi
if [ [ " $@ " = ~ " --clean " ] ] ; then log " ${ BLUE } [Info] Cleaning up... ${ NC } " ; rm -rf " $BUILD_DIR " " $LOGFILE " ; fi
2025-07-29 09:05:47 -04:00
2025-07-29 09:25:44 -04:00
echo "" > " $LOGFILE " # Clear log file
log "--- GridFire Installation Log ---"
2025-07-29 09:05:47 -04:00
log " Date: $( date) "
log " OS: ${ OS_NAME } , Distro: ${ DISTRO_ID } "
2025-07-29 09:25:44 -04:00
if [ [ " $@ " = ~ " --tui " ] ] ; then
run_main_tui
log " ${ GREEN } Exited TUI mode. ${ NC } "
2025-07-29 09:05:47 -04:00
exit 0
fi
2025-07-29 09:25:44 -04:00
# --- Non-TUI path ---
log " \n ${ BLUE } --- Checking System Dependencies (CLI Mode) --- ${ NC } "
2025-07-29 09:44:29 -04:00
# Run check_compiler first to set the default
check_compiler
2025-07-29 09:25:44 -04:00
declare -A CHECKS = (
2025-07-29 09:44:29 -04:00
[ python-dev] = "check_python_dev" [ meson-python] = "check_meson_python" [ cmake] = "check_cmake"
[ meson] = "check_meson" [ boost] = "check_boost"
2025-07-29 09:25:44 -04:00
)
2025-07-29 09:44:29 -04:00
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
2025-07-29 09:25:44 -04:00
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
2025-07-29 09:44:29 -04:00
eval " $install_cmd " 2>& 1 | tee -a " $LOGFILE "
2025-07-29 09:25:44 -04:00
fi
else
log " ${ RED } [Error] No automatic installation for ' ${ dep } '. Please install manually. ${ NC } "
2025-07-29 09:05:47 -04:00
fi
fi
2025-07-29 09:25:44 -04:00
done
2025-07-29 09:05:47 -04:00
2025-07-29 09:25:44 -04:00
log " \n ${ BLUE } --- Re-checking all dependencies --- ${ NC } "
local final_fail = false
2025-07-29 09:44:29 -04:00
# Re-add compiler check to the list for final verification
CHECKS[ compiler] = "check_compiler"
2025-07-29 09:25:44 -04:00
for dep in " ${ !CHECKS[@] } " ; do
if ! ${ CHECKS [ $dep ] } ; then
log " ${ RED } [FATAL] Dependency still missing: ${ dep } ${ NC } "
final_fail = true
fi
2025-07-29 09:05:47 -04:00
done
2025-07-29 09:25:44 -04:00
if $final_fail ; then
log " ${ RED } Please install missing dependencies and re-run. ${ NC } "
exit 1
2025-07-29 09:05:47 -04:00
fi
2025-07-29 09:25:44 -04:00
log " \n ${ GREEN } --- All dependencies met. Proceeding with build. --- ${ NC } "
run_meson_setup && run_meson_compile
2025-07-29 09:05:47 -04:00
}
main " $@ "