diff --git a/build-config/boost/meson.build b/build-config/boost/meson.build index ec6d5cba..75467a8c 100644 --- a/build-config/boost/meson.build +++ b/build-config/boost/meson.build @@ -1,10 +1,3 @@ -boost_dep = dependency('boost', version: '>=1.8.0') - -if not boost_dep.found() - message('❌ Boost library not found. Note that Boost is a system dependency and must be installed seperatly.') - message('GridFire includes a Boost auto installation script, attempting to run it now (this will require some user input)...') - run_res = run_command('./install.sh', check: true) - boost_dep = dependency('boost', version: '>=1.8.0', required: true) -endif +boost_dep = dependency('boost', version: '>=1.8.0', required: true) diff --git a/install.sh b/install.sh new file mode 100755 index 00000000..1bd38c71 --- /dev/null +++ b/install.sh @@ -0,0 +1,410 @@ +#!/usr/bin/env bash +# +# install.sh - Comprehensive interactive installation script for GridFire +# +# This script performs the following actions: +# 1. Checks for essential system dependencies: +# - A C++ compiler (g++ or clang++) +# - Python 3 development headers +# - CMake +# - Meson +# - Boost libraries +# 2. If run with the --tui flag, it provides a text-based user interface +# to select and install missing dependencies. +# 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 runs the meson setup and build commands. +# 6. Logs all operations to a file for easy debugging. + +set -o pipefail + +# --- Configuration --- +LOGFILE="GridFire_Installer.log" +BUILD_DIR="build" + +# --- 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" +} + +# Prompt the user for a yes/no answer. +prompt_yes_no() { + local prompt_msg="$1" + local answer + while true; do + read -p "$(echo -e "${YELLOW}${prompt_msg}${NC}") " answer + case "$answer" in + [Yy]* ) return 0;; + [Nn]* ) return 1;; + * ) echo "Please answer yes or no.";; + esac + done +} + +# 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." + 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 --- + +# Check for a C++ compiler. +check_compiler() { + if command -v g++ &>/dev/null; then + COMPILER_CMD="g++" + log "${GREEN}[OK] Found C++ compiler: $(g++ --version | head -n1)${NC}" + return 0 + elif command -v clang++ &>/dev/null; then + COMPILER_CMD="clang++" + log "${GREEN}[OK] Found C++ compiler: $(clang++ --version | head -n1)${NC}" + return 0 + else + log "${RED}[FAIL] No C++ compiler (g++ or clang++) found.${NC}" + return 1 + fi +} + +# Check for Python 3 development headers. +check_python_dev() { + if 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 for CMake. +check_cmake() { + if command -v cmake &>/dev/null; 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 for Meson. +check_meson() { + if command -v meson &>/dev/null; then + log "${GREEN}[OK] Found Meson: $(meson --version)${NC}" + return 0 + else + log "${RED}[FAIL] Meson not found.${NC}" + return 1 + fi +} + +# Check if Boost is installed and available to Meson. +check_boost() { + log "${BLUE}[Info] Checking for Boost dependency using Meson...${NC}" + if meson introspect --dependencies -C "$BUILD_DIR" 2>/dev/null | grep -q "boost.*found: YES"; then + log "${GREEN}[OK] Boost dependency is already met in the Meson build directory.${NC}" + return 0 + fi + + # A simple meson dependency check as a fallback + local test_dir="meson-boost-test" + rm -rf "$test_dir" && mkdir "$test_dir" + cat > "$test_dir/meson.build" </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 --- + +# Execute an installation command. +run_install_cmd() { + local cmd="$1" + log "${BLUE}[Exec] Running: ${cmd}${NC}" + if prompt_yes_no "Execute this command? (y/n):"; then + eval "${cmd}" 2>&1 | tee -a "$LOGFILE" + return $? + else + log "${YELLOW}[Skip] User skipped installation command.${NC}" + return 1 + fi +} + +# Get installation command for a given dependency. +get_install_cmd() { + local dep_name="$1" + local cmd="" + case "$OS_NAME" in + "macOS") + case "$dep_name" in + "compiler") cmd="xcode-select --install" ;; + "python-dev") cmd="brew install python3" ;; + "cmake") cmd="brew install cmake" ;; + "meson") cmd="brew install meson" ;; + "boost") cmd="brew install boost" ;; + "dialog") cmd="brew install dialog" ;; + esac + ;; + "Linux") + case "$DISTRO_ID" in + "ubuntu"|"debian"|"linuxmint") + case "$dep_name" in + "compiler") cmd="sudo apt-get install -y build-essential" ;; + "python-dev") cmd="sudo apt-get install -y python3-dev" ;; + "cmake") cmd="sudo apt-get install -y cmake" ;; + "meson") cmd="sudo apt-get install -y meson" ;; + "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++" ;; + "python-dev") cmd="sudo dnf install -y python3-devel" ;; + "cmake") cmd="sudo dnf install -y cmake" ;; + "meson") cmd="sudo dnf install -y meson" ;; + "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 base-devel" ;; + "python-dev") cmd="sudo pacman -S --noconfirm python" ;; + "cmake") cmd="sudo pacman -S --noconfirm cmake" ;; + "meson") cmd="sudo pacman -S --noconfirm meson" ;; + "boost") cmd="sudo pacman -S --noconfirm boost" ;; + "dialog") cmd="sudo pacman -S --noconfirm dialog" ;; + esac + ;; + *) + log "${YELLOW}[Warn] Unsupported Linux distribution: ${DISTRO_ID}. Cannot provide install commands.${NC}" + ;; + esac + ;; + *) + log "${YELLOW}[Warn] Unsupported OS: ${OS_NAME}. Cannot provide install commands.${NC}" + ;; + esac + echo "$cmd" +} + +# --- Main Logic --- + +# Run the Meson setup and build process. +run_meson_build() { + log "\n${BLUE}--- Starting Meson Build ---${NC}" + if [ ! -f "meson.build" ]; then + log "${RED}[FATAL] meson.build file not found in the current directory. Cannot proceed.${NC}" + exit 1 + fi + + log "${BLUE}[Info] Running meson setup...${NC}" + if ! meson setup "${BUILD_DIR}"; then + log "${RED}[FATAL] Meson setup failed. Check the output above and in ${LOGFILE} for details.${NC}" + exit 1 + fi + log "${GREEN}[Success] Meson setup complete.${NC}" + + log "${BLUE}[Info] Running meson compile...${NC}" + if ! meson compile -C "${BUILD_DIR}"; then + log "${RED}[FATAL] Meson compile failed. Check the output above and in ${LOGFILE} for details.${NC}" + exit 1 + fi + + log "\n${GREEN}--- Build Successful! ---${NC}" + log "${GREEN}The project has been built in the '${BUILD_DIR}' directory.${NC}" +} + +# Run the TUI for dependency installation. +run_tui() { + if ! command -v dialog &>/dev/null; then + log "${YELLOW}[Warn] The 'dialog' utility is not installed. It is required for TUI mode.${NC}" + local install_cmd + install_cmd=$(get_install_cmd "dialog") + if [ -n "$install_cmd" ]; then + if prompt_yes_no "Would you like to install it now? (y/n):"; then + eval "$install_cmd" + if ! command -v dialog &>/dev/null; then + log "${RED}[FATAL] Failed to install 'dialog'. Cannot run in TUI mode.${NC}" + exit 1 + fi + else + log "${RED}[FATAL] Cannot run in TUI mode without 'dialog'. Exiting.${NC}" + exit 1 + fi + else + log "${RED}[FATAL] Cannot automatically install 'dialog' on your system. Please install it manually and try again.${NC}" + exit 1 + fi + fi + + local choices + choices=$(dialog --clear --backtitle "Project Dependency Installer" \ + --title "Install Dependencies" \ + --checklist "Select dependencies to install. Dependencies marked with (*) are missing." 20 70 5 \ + "compiler" "C++ Compiler (g++ or clang++)" "$([[ ${DEP_STATUS[compiler]} == "FAIL" ]] && echo "on" || echo "off")" \ + "python-dev" "Python 3 Dev Headers" "$([[ ${DEP_STATUS[python-dev]} == "FAIL" ]] && echo "on" || echo "off")" \ + "cmake" "CMake" "$([[ ${DEP_STATUS[cmake]} == "FAIL" ]] && echo "on" || echo "off")" \ + "meson" "Meson Build System" "$([[ ${DEP_STATUS[meson]} == "FAIL" ]] && echo "on" || echo "off")" \ + "boost" "Boost Libraries" "$([[ ${DEP_STATUS[boost]} == "FAIL" ]] && echo "on" || echo "off")" \ + 3>&1 1>&2 2>&3) + + clear + + if [ -z "$choices" ]; then + log "${YELLOW}[Info] No dependencies selected for installation.${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 + run_install_cmd "$install_cmd" + else + log "${RED}[Error] No automatic installation command available for '${dep}' on your system.${NC}" + log "${RED}Please install it manually.${NC}" + fi + done +} + +# --- Script Entry Point --- +main() { + # Handle command-line arguments + if [[ " $@ " =~ " --help " ]] || [[ " $@ " =~ " -h " ]]; then + show_help + exit 0 + fi + + if [[ " $@ " =~ " --clean " ]]; then + log "${BLUE}[Info] Cleaning up previous build...${NC}" + rm -rf "$BUILD_DIR" "$LOGFILE" + fi + + # Start logging + echo "" > "$LOGFILE" + log "--- Project Installation Log ---" + log "Date: $(date)" + log "OS: ${OS_NAME}, Distro: ${DISTRO_ID}" + + # Perform all dependency checks + log "\n${BLUE}--- Checking System Dependencies ---${NC}" + declare -A DEP_STATUS + check_compiler; DEP_STATUS[compiler]=$? + check_python_dev; DEP_STATUS[python-dev]=$? + check_cmake; DEP_STATUS[cmake]=$? + check_meson; DEP_STATUS[meson]=$? + check_boost; DEP_STATUS[boost]=$? + + local all_deps_met=0 + for status in "${DEP_STATUS[@]}"; do + if [ "$status" -ne 0 ]; then + all_deps_met=1 + break + fi + done + + if [ $all_deps_met -eq 0 ]; then + log "\n${GREEN}--- All dependencies are met! ---${NC}" + run_meson_build + exit 0 + fi + + log "\n${YELLOW}--- Some dependencies are missing ---${NC}" + + if [[ " $@ " =~ " --tui " ]]; then + run_tui + else + # Interactive command-line prompts + for dep in "${!DEP_STATUS[@]}"; do + if [ "${DEP_STATUS[$dep]}" -ne 0 ]; 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 it now? (y/n):"; then + run_install_cmd "$install_cmd" + fi + else + log "${RED}[Error] No automatic installation command available for '${dep}' on your system. Please install it manually.${NC}" + fi + fi + done + fi + + # Final check before build + log "\n${BLUE}--- Re-checking dependencies after installation attempts ---${NC}" + check_compiler; DEP_STATUS[compiler]=$? + check_python_dev; DEP_STATUS[python-dev]=$? + check_cmake; DEP_STATUS[cmake]=$? + check_meson; DEP_STATUS[meson]=$? + check_boost; DEP_STATUS[boost]=$? + + all_deps_met=0 + local failed_deps="" + for dep in "${!DEP_STATUS[@]}"; do + if [ "${DEP_STATUS[$dep]}" -ne 0 ]; then + all_deps_met=1 + failed_deps+="${dep} " + fi + done + + if [ $all_deps_met -eq 0 ]; then + log "\n${GREEN}--- All dependencies are now met! ---${NC}" + run_meson_build + else + log "\n${RED}[FATAL] The following dependencies are still missing: ${failed_deps}${NC}" + log "${RED}Please install them manually and re-run this script.${NC}" + log "${RED}Check ${LOGFILE} for detailed error messages.${NC}" + exit 1 + fi +} + +# Run the main function +main "$@"