From 443f9e6888748902133de7811bc6d638db32aa54 Mon Sep 17 00:00:00 2001 From: Emily Boudreaux Date: Tue, 29 Jul 2025 13:44:43 -0400 Subject: [PATCH] build(install): added venv python managment --- README.md | 19 +++++++- install.sh | 132 +++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 135 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 3f1828fe..59f0734c 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,8 @@ GridFire is a part of the 4D-STAR collaboration. Work for this project is funded by the European Union. Views and opinions expressed are however those of the author(s) only and do not necessarily reflect those of the European Union or the European Research Council. -## Build and Installation Instructions +## Automatic Build and Installation +### Script Build and Installation Instructions The easiest way to build GridFire is using the `install.sh` or `install-tui.sh` scripts in the root directory. To use these scripts, simply run: @@ -41,14 +42,28 @@ these scripts, simply run: # or ./install-tui.sh ``` - The regular installation script will select a standard "ideal" set of build options for you. If you want more control over the build options, you can use the `install-tui.sh` script, which will provide a text-based user interface to select the build options you want. Generally, both are intended to be easy to use and will prompt you automatically to install any missing dependencies. + +### Currently known good platforms +The installation script has been tested and found to work on clean installations of the following platforms: +- MacOS 15.3.2 (Apple Silicon + brew installed) +- Fedora 42.0 (aarch64) +- Ubuntu 25.04 (aarch64) +- Ubuntu 22.04 (X86_64) + +> **Note:** On Ubuntu 22.04 the user needs to install boost libraries manually as the versions in the Ubuntu repositories +are too old. The installer automatically detects this and will instruct the user in how to do this. + +## Manual Build Instructions + ### Prerequisites +These only need to be manually installed if the user is not making use of the `install.sh` or `install-tui.sh` + - C++ compiler supporting C++23 standard - Meson build system (>= 1.5.0) - Python 3.10 or newer diff --git a/install.sh b/install.sh index bd8f17cf..80df8f59 100755 --- a/install.sh +++ b/install.sh @@ -17,6 +17,7 @@ set -o pipefail LOGFILE="GridFire_Installer.log" NOTES_FILE="notes.txt" CONFIG_FILE="gridfire_build.conf" +VENV_DIR=".venv" MIN_GCC_VER="13.0.0" MIN_CLANG_VER="16.0.0" MIN_MESON_VER="1.5.0" @@ -146,6 +147,25 @@ check_command() { command -v "$1" &>/dev/null } +is_externally_managed() { + # Check for the PEP 668 marker file + local py_prefix + py_prefix=$(python3 -c "import sys; print(sys.prefix)") + if [ -f "$py_prefix/EXTERNALLY-MANAGED" ]; then + return 0 # 0 means true in bash + else + return 1 # 1 means false + fi +} + +get_pip_cmd() { + if [ -d "$VENV_DIR" ]; then + echo "$VENV_DIR/bin/pip" + else + echo "python3 -m pip" + fi +} + set_compilers() { if [[ "$CC_COMPILER" == *"clang++"* ]]; then C_COMPILER=$(echo "$CC_COMPILER" | sed 's/clang++/clang/') @@ -227,6 +247,16 @@ check_compiler() { fi } +check_pip() { + if python3 -m pip --version &>/dev/null; then + log "${GREEN}[OK] Found pip.${NC}" + return 0 + else + log "${RED}[FAIL] pip not found.${NC}" + 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}" @@ -238,6 +268,12 @@ check_python_dev() { } check_meson_python() { + if [ -d "$VENV_DIR" ]; then + if "$VENV_DIR/bin/python3" -c "import mesonpy" &>/dev/null; then + log "${GREEN}[OK] Found meson-python package (in venv).${NC}" + return 0 + fi + fi if python3 -c "import mesonpy" &>/dev/null; then log "${GREEN}[OK] Found meson-python package.${NC}" return 0 @@ -258,8 +294,13 @@ check_cmake() { } check_meson() { - if check_command meson; then - local ver; ver=$(meson --version) + local meson_cmd="meson" + if [ -d "$VENV_DIR" ]; then + meson_cmd="$VENV_DIR/bin/meson" + fi + + if check_command "$meson_cmd"; then + local ver; ver=$($meson_cmd --version) vercomp "$ver" "$MIN_MESON_VER" if [[ $? -ne 2 ]]; then log "${GREEN}[OK] Found Meson ${ver}.${NC}" @@ -383,6 +424,7 @@ get_install_cmd() { case "$dep_name" in "compiler") cmd="$brew_cmd install gcc llvm" ;; # Install both "python-dev") cmd="$brew_cmd install python3" ;; + "pip") cmd="python3 -m ensurepip --upgrade" ;; "meson-python") cmd="python3 -m pip install meson-python" ;; "meson") cmd="python3 -m pip install --upgrade meson" ;; "cmake") cmd="$brew_cmd install cmake" ;; @@ -396,6 +438,7 @@ get_install_cmd() { 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" ;; + "pip") cmd="sudo apt-get install -y python3-pip" ;; "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" ;; @@ -407,6 +450,7 @@ get_install_cmd() { case "$dep_name" in "compiler") cmd="sudo dnf install -y gcc-c++ gcc-gfortran clang" ;; "python-dev") cmd="sudo dnf install -y python3-devel" ;; + "pip") cmd="sudo dnf install -y python3-pip" ;; "meson-python") cmd="python3 -m pip install meson-python" ;; "meson") cmd="python3 -m pip install --upgrade meson" ;; "cmake") cmd="sudo dnf install -y cmake" ;; @@ -418,6 +462,7 @@ get_install_cmd() { case "$dep_name" in "compiler") cmd="sudo pacman -S --noconfirm gcc gcc-fortran clang" ;; "python-dev") cmd="sudo pacman -S --noconfirm python" ;; + "pip") cmd="sudo pacman -S --noconfirm python-pip" ;; "meson-python") cmd="python3 -m pip install meson-python" ;; "meson") cmd="python3 -m pip install --upgrade meson" ;; "cmake") cmd="sudo pacman -S --noconfirm cmake" ;; @@ -533,10 +578,27 @@ check_dialog_installed() { return 0 } +ensure_venv() { + if [ ! -d "$VENV_DIR" ]; then + if dialog --title "Virtual Environment" --yesno "A local Python virtual environment ('${VENV_DIR}') is required for this action. Create it now?" 8 70; then + log "${BLUE}[Info] Creating Python virtual environment in '${VENV_DIR}'...${NC}" + if ! python3 -m venv "$VENV_DIR"; then + dialog --msgbox "Failed to create virtual environment. Please ensure 'python3-venv' is installed." 8 60 + return 1 + fi + log "${GREEN}[Success] Virtual environment created.${NC}" + else + 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_pip >/dev/null; DEP_STATUS[pip]=$? 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]=$? @@ -548,8 +610,9 @@ run_dependency_installer_tui() { 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 \ + --checklist "Select dependencies to install. Already found dependencies are unchecked." 20 70 7 \ "compiler" "C++ Compilers (g++, clang++)" "$([[ ${DEP_STATUS[compiler]} -ne 0 ]] && echo "on" || echo "off")" \ + "pip" "Python Package Installer (pip)" "$([[ ${DEP_STATUS[pip]} -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")" \ @@ -563,19 +626,30 @@ run_dependency_installer_tui() { 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" + + # Handle python packages specially + if [[ "$dep" == "meson-python" || "$dep" == "meson" ]]; then + if is_externally_managed; then + if ! ensure_venv; then + log "${YELLOW}[Skip] User cancelled venv creation. Skipping ${dep} installation.${NC}" + continue + fi + fi + local pip_cmd; pip_cmd=$(get_pip_cmd) + eval "$pip_cmd install --upgrade $dep" 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 + 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 fi done # Re-run check to update status check_compiler - 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=false # Force re-check of boost } run_python_bindings_tui() { @@ -595,6 +669,33 @@ run_python_bindings_tui() { return fi + local pip_cmd="python3 -m pip" + local pip_opts="" + + if is_externally_managed; then + local env_choice + env_choice=$(dialog --clear --backtitle "Python Environment" \ + --title "Externally Managed Environment Detected" \ + --menu "This OS protects its system Python. How would you like to install the bindings?" 15 78 3 \ + "1" "Use a Virtual Environment (Recommended)" \ + "2" "Install to System with --break-system-packages (Advanced)" \ + "3" "Cancel" \ + 3>&1 1>&2 2>&3) + + case "$env_choice" in + 1) + if ! ensure_venv; then return; fi + pip_cmd="$VENV_DIR/bin/pip" + ;; + 2) + pip_opts="--break-system-packages" + ;; + *) + return + ;; + esac + fi + local choice choice=$(dialog --clear --backtitle "Python Bindings Installer" \ --title "Install Python Bindings" \ @@ -607,7 +708,7 @@ run_python_bindings_tui() { 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 + if ! CC="${C_COMPILER}" CXX="${CC_COMPILER}" FC="${FC_COMPILER}" $pip_cmd install $pip_opts -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 @@ -617,7 +718,7 @@ run_python_bindings_tui() { ;; 2) log "${BLUE}[Info] Installing Python bindings in User Mode...${NC}" - if ! CC="${C_COMPILER}" CXX="${CC_COMPILER}" FC="${FC_COMPILER}" pip install .; then + if ! CC="${C_COMPILER}" CXX="${CC_COMPILER}" FC="${FC_COMPILER}" $pip_cmd install $pip_opts .; 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 @@ -1062,7 +1163,10 @@ run_main_tui() { clear case "$choice" in - 1) run_dependency_installer_tui ;; + 1) + run_dependency_installer_tui + BOOST_CHECKED=false # Force re-check after installing + ;; 2) run_build_config_tui ;; 3) run_python_bindings_tui ;; 4) run_meson_setup && run_meson_compile ;;