diff --git a/mfc.sh b/mfc.sh index d4aea5b97..1b5eb4a08 100755 --- a/mfc.sh +++ b/mfc.sh @@ -1,474 +1,53 @@ #!/bin/bash -unset u_c -unset u_cg - -MFC_PYTHON_MIN_MAJOR=3 -MFC_PYTHON_MIN_MINOR=8 -MFC_PYTHON_MIN_STR="$MFC_PYTHON_MIN_MAJOR.$MFC_PYTHON_MIN_MINOR" - # Check whether this script was called from MFC's root directory. -if [ ! -f "$(pwd)/toolchain/mfc.py" ]; then +if [ ! -f "$(pwd)/toolchain/util.sh" ]; then echo "mfc: ERROR > You must call this script from within MFC's root folder." exit 1 fi # Load utility script -source "$(pwd)/toolchain/util.sh" +. "$(pwd)/toolchain/util.sh" # Handle upgrading from older MFC build systems if [ -d "$(pwd)/bootstrap" ] || [ -d "$(pwd)/dependencies" ] || [ -f "$(pwd)/build/mfc.lock.yaml" ]; then - error "You are upgrading from an older version of$MAGENTA MFC$COLOR_RESET. Please remove, if applicable, the dependencies/, bootstrap/, and build/ directories before running this command again." + error "Please remove, if applicable, the following directories:" + error "* dependencies/" + error "* bootstrap/" + error "* build/" + error "These are incompatible with modern versions of MFC." exit 1 fi # If the user wishes to run the "load" script if [ "$1" == 'load' ]; then - shift; - - # Reset u_computer & u_cg to known values since this script is run with "source" - # Therefore, values of variables defined here are kept when the script runs again. - u_computer=""; u_cg="" - - # If there are command-line arguments, parse them: - while [[ $# -gt 0 ]]; do - case $1 in - -c|--computer) u_c="$2"; shift; shift; ;; - -m|--mode) u_cg="$2"; shift; shift; ;; - -*|--*) echo "Unknown option $1"; return; ;; - esac - done - - # Get computer (if not supplied in command-line) - if [ -v $u_c ]; then - log "Select a system:" - log "$G""ORNL$W: Ascent (a) | Crusher (c) | Summit (s) | Wombat (w)" - log "$C""ACCESS$W: Bridges2 (b) | Expanse (e) | Delta (d)" - log "$Y""GaTech$W: Phoenix (p)" - log "$R""CALTECH$W: Richardson (r)" - log_n "($G""a$W/$G""c$W/$G""s$W/$G""w$W/$C""b$W/$C""e$CR/$C""d$CR/$Y""p$CR/$R""r$CR): " - read u_c - log - fi - - # Get CPU/GPU (if not supplied in command-line) - if [ -v $u_cg ]; then - log "Select configuration:" - log " - CPU (c | cpu)" - log " - GPU (g | gpu)" - log_n "(c/cpu/g/gpu): " - read u_cg - log - fi - - # User input to lowercase - u_c=$(echo "$u_c" | tr '[:upper:]' '[:lower:]') - u_cg=$(echo "$u_cg" | tr '[:upper:]' '[:lower:]') - - if [ "$u_cg" == 'c' ] || [ "$u_cg" == 'cpu' ]; then - CG='CPU'; cg='cpu' - elif [ "$u_cg" == "g" ] || [ "$u_cg" == 'gpu' ]; then - CG='GPU'; cg='gpu' - fi - - __combine() { - echo -n $@ | sed 's/\[\n\r\s\]\+/\ /' - } - - __extract() { - __combine "$(grep -E "^$1\s+" toolchain/modules | sed "s/^$1\s\+//")" - } - - COMPUTER="$(__extract "$u_c")" - - if [[ -z "$COMPUTER" ]]; then - error "Computer $M$u_cg$CR not recognized." - return - fi - - MODULES=($(__extract "$u_c-$cg") $(__combine $(__extract "$u_c-all"))) - - log "Loading modules for $M$COMPUTER$CR on $M$CG$CR"'s:' - - # Reset modules to default system configuration - if [ "$u_c" != 'p' ]; then - module reset > /dev/null 2>&1 - code="$?" - - # Purge if reset is not available - if [ "$code" -ne '0' ]; then - module purge > /dev/null 2>&1 - fi - else - module purge > /dev/null 2>&1 - fi - - # Find length of longest module_name in $MODULES for $COMPUTER - max_module_length="0" - for module_name in ${MODULES[@]}; do - module_length="${#module_name}" - - if [ "$module_length" -gt "$max_module_length" ]; then - max_module_length="$module_length" - fi - done - - # Load modules ($MODULES) - for module_name in ${MODULES[@]}; do - log_n " - $CYAN$module_name$COLOR_RESET " - - # Add padding spaces - module_length="${#module_name}" - delta="$((max_module_length-module_length-1))" - if [ "$delta" -ge "0" ]; then - printf "%0.s " $(seq 0 $delta) - fi - - # Load the module - module load "$module_name" > /dev/null 2>&1 - - # Handle Success / Failure - code=$? - if [ "$code" == '0' ]; then - echo -e "[$G""SUCCESS$W]" - else - echo -e "[$R""FAILURE$W]" - - # Run load again to show error message - module load "$module_name" - - return - fi - done - - if [ "$cg" == 'gpu' ]; then - isnv=$($FC --version | grep NVIDIA | wc -l) - if [ $isnv -eq 0 ]; then - export CC=nvc CXX=nvc++ FC=nvfortran - fi - fi - ok 'All modules and environment variables have been loaded.' - - - return + shift; . "$(pwd)/toolchain/bootstrap/modules.sh" $@; return elif [ "$1" == "format" ]; then - shift - - if ! command -v fprettify > /dev/null 2>&1; then - pip3 install --upgrade fprettify - fi - - fprettify ${@:-src} --exclude "src/*/autogen" --recursive --silent \ - --indent 4 --c-relations --enable-replacements --enable-decl \ - --whitespace-comma 1 --whitespace-multdiv 1 --whitespace-plusminus 1 \ - --case 1 1 1 1 --strict-indent - ret="$?" - - if [ "$ret" != '0' ]; then - error "failed to execute fprettify." - error "MFC has not been fprettify'ied." - - exit 1 - fi + . "$(pwd)/toolchain/bootstrap/python.sh" - ok "MFC has been fprettify'ied." - - exit 0 + shift; . "$(pwd)/toolchain/bootstrap/format.sh" $@; exit 0 elif [ "$1" == "docker" ]; then - shift; - - if ! command -v docker > /dev/null 2>&1; then - error "$MAGENTA""Docker$COLOR_RESET is not installed." - - exit 1 - fi - - log "Running in$MAGENTA Docker$COLOR_RESET mode." - - if [ -t 1 ]; then - dockerintopts='--interactive --tty' - fi - - __docker_run() { - docker run $dockerintopts --rm --workdir /home/me/MFC \ - --mount type=bind,source="$(pwd)",target=/home/me/MFC \ - sbryngelson/mfc:latest $@ - } - - __docker_run sudo chown -R me:me /home/me/MFC - if (($?)); then - error "Docker: Failed to set directory permissions on MFC mount.." - - exit 1 - fi - - __docker_run $@ - if (($?)); then - error "Error running Docker container with $@." - - exit 1 - fi - - exit 0 + shift; . "$(pwd)/toolchain/bootstrap/docker.sh" $@; exit 0 fi - mkdir -p "$(pwd)/build" - -# If we downloaded our own version of CMake, let us make it accessible to -# any command we run hereafter. - -if [ -f "$(pwd)/build/cmake/bin/cmake" ]; then - export PATH="$(pwd)/build/cmake/bin:$PATH" - - log "Found$MAGENTA CMake$COLOR_RESET: $MAGENTA$(pwd)/build/cmake/bin/cmake$COLOR_RESET." -fi - -bShouldInstallCMake=false -if ! command -v cmake > /dev/null 2>&1; then - # Not installed - bShouldInstallCMake=true - - warn "$MAGENTA""CMake$COLOR_RESET is not installed." -else - cmake_verstr=$(cmake --version | tr ' ' '\n' | sed -n 3p) - cmake_major=$(echo $cmake_verstr | tr '.' '\n' | sed -n 1p) - cmake_minor=$(echo $cmake_verstr | tr '.' '\n' | sed -n 2p) - cmake_patch=$(echo $cmake_verstr | tr '.' '\n' | sed -n 3p) - cmake_version="$(printf %05d%05d%05d $cmake_major $cmake_minor $cmake_patch)" - - MFC_CMAKE_MIN_VERSTR=$(cat CMakeLists.txt | grep cmake_minimum_required | head -n 1 | sed 's/[^0-9,.]*//g') - MFC_CMAKE_MIN_MAJOR=$(echo $MFC_CMAKE_MIN_VERSTR | tr '.' '\n' | head -n 1) - MFC_CMAKE_MIN_MINOR=$(echo $MFC_CMAKE_MIN_VERSTR | tr '.' '\n' | head -n 2 | tail -n 1) - MFC_CMAKE_MIN_PATCH=0 - MFC_CMAKE_MIN_VERSION="$(printf %05d%05d%05d $MFC_CMAKE_MIN_MAJOR $MFC_CMAKE_MIN_MINOR $MFC_CMAKE_MIN_PATCH)" - - if [ "$cmake_version" -lt "$MFC_CMAKE_MIN_VERSION" ]; then - # Out of date - bShouldInstallCMake=true - - log "$MAGENTA CMake$YELLOW is out of date (current: $MAGENTA$cmake_verstr$YELLOW < minimum: $MAGENTA$MFC_CMAKE_MIN_VERSTR$YELLOW).$COLOR_RESET" - fi -fi - -if [ "$bShouldInstallCMake" = true ]; then - version="3.25.2" - arch="$(uname -m)" - - bErrorDoSelfDownload=false - if [[ "$OSTYPE" == "darwin"* ]]; then - error "Cannot fetch$MAGENTA CMake$COLOR_RESET for$MAGENTA macOS$COLOR_RESET." - bErrorDoSelfDownload=true - fi - - if ! [[ "$arch" == "x86_64" || "$arch" == "aarch64" ]]; then - error "Cannot fetch$MAGENTA CMake$COLOR_RESET for architecture $MAGENTA$arch$COLOR_RESET." - bErrorDoSelfDownload=true - fi - - if [ "$bErrorDoSelfDownload" = true ]; then - log "Please install$MAGENTA CMake$COLOR_RESET manually:" - log " - via$MAGENTA Aptitude$COLOR_RESET: $ sudo apt install cmake" - log " - via$MAGENTA Homebrew$COLOR_RESET: $ brew install cmake" - log " - via$MAGENTA Spack$COLOR_RESET: $ spack install cmake" - log " - via$MAGENTA Pacman$COLOR_RESET: $ sudo pacman -S cmake" - log " - via$MAGENTA Lmod$COLOR_RESET: $ module load cmake (if available)" - log " - via$MAGENTA cmake.org$COLOR_RESET: https://cmake.org/download/" - - exit 1 - fi - - if [ -d "$(pwd)/build/cmake" ]; then - rm -rf "$(pwd)/build/cmake" - fi - - mkdir -p "$(pwd)/build/cmake" - - filename="cmake-$version-linux-$arch.sh" - repository="https://github.com/Kitware/CMake" - url="$repository/releases/download/v$version/$filename" - - log "Downloading$MAGENTA CMake v$version$COLOR_RESET for $MAGENTA$arch$COLOR_RESET from $CYAN$repository$COLOR_RESET." - - cmake_fatal_error() { - log "$MAGENTA""CMake$COLOR_RESET is not discoverable or is an older release, incompatible with$MAGENTA MFC$COLOR_RESET. Please download" - log "or install a recent version of$MAGENTA CMake$COLOR_RESET to get past this step. If you are currently on a" - log "managed system like a cluster, provided there is no suitable environment module, you can" - log "either build it from source, or get it via$MAGENTA Spack$COLOR_RESET." - log "- The minimum required version is currently$MAGENTA CMake$COLOR_RESET v$MFC_CMAKE_MIN_MAJOR.$MFC_CMAKE_MIN_MINOR.$MFC_CMAKE_MIN_PATCH." - log "- We attempted to download$MAGENTA CMake$COLOR_RESET v$version from $url." - - exit 1 - } - - if ! command -v wget > /dev/null 2>&1; then - error "$MAGENTA""wget$COLOR_RESET is not installed but is necessary to download$MAGENTA CMake$COLOR_RESET." - - cmake_fatal_error - fi - - if ! wget -P "$(pwd)/build/cmake" "$url"; then - error "Failed to download a compatible version of$MAGENTA CMake$COLOR_RESET." - - cmake_fatal_error - fi - - log "Installing$MAGENTA CMake$COLOR_RESET into $MAGENTA$(pwd)/build/cmake$COLOR_RESET." - - if ! $SHELL "$(pwd)/build/cmake/$filename" "--skip-license" "--prefix=$(pwd)/build/cmake"; then - error "Failed to install a compatible version of CMake." - - cmake_fatal_error - fi - - rm "$(pwd)/build/cmake/$filename" - - ok "Found$MAGENTA CMake$COLOR_RESET: $MAGENTA$(pwd)/build/cmake/bin/cmake$COLOR_RESET." - export PATH="$(pwd)/build/cmake/bin:$PATH" -fi - - -is_python_compatible() { - if ! ${1:-python3} -c "import sys; exit(int(not (sys.version_info[0]==$MFC_PYTHON_MIN_MAJOR and sys.version_info[1] >= $MFC_PYTHON_MIN_MINOR)))"; then - return 1 - fi - - return 0 -} - - -assert_python_compatible() { - if ! is_python_compatible $1; then - error "$MAGENTA$(${1:-python3} --version)$COLOR_RESET (${1:-python3}) is out of date. Required >= $MAGENTA$MFC_PYTHON_MIN_STR$COLOR_RESET." - - exit 1 - fi -} - - -if [ -f "$(pwd)/build/venv/bin/activate" ]; then - if ! is_python_compatible "$(pwd)/build/venv/bin/python3"; then - warn "$MAGENTA""Python$COLOR_RESET is outdated inside the Virtualenv." - warn "Deleting the Virtualenv and starting from scratch..." - - rm -r "$(pwd)/build/venv" - fi -fi - -if ! command -v pip3 > /dev/null 2>&1 && [ ! -f "$(pwd)/build/venv/bin/activate" ]; then - # Check whether python3 is in the $PATH / is accessible. - if ! command -v python3 > /dev/null 2>&1; then - error "Couldn't find$MAGENTA Python$COLOR_RESET. Please ensure it is discoverable." - - exit 1 - fi - - assert_python_compatible - - get_pip_url="https://bootstrap.pypa.io/pip/get-pip.py" - - warn "$MAGENTA""Python$COLOR_RESET's$MAGENTA PIP$COLOR_RESET is not installed." - log "Downloading$MAGENTA Python$COLOR_RESET's$MAGENTA PIP$COLOR_RESET from $get_pip_url..." - - if ! wget -O "$(pwd)/build/get-pip.py" "$get_pip_url"; then - error "Couldn't download get-pip.py." - - exit 1 - fi - - # Suppress PIP version warning (out of date) - export PIP_DISABLE_PIP_VERSION_CHECK=1 - if ! python3 "$(pwd)/build/get-pip.py" --user; then - error "Couldn't install$MAGENTA pip$COLOR_RESET with get-pip.py" - - exit 1 - fi - - ok "Installed pip." -fi - - -# Create a Python virtualenv if it hasn't already been created -if [ ! -f "$(pwd)/build/venv/bin/activate" ]; then - assert_python_compatible - - if ! python3 -m venv "$(pwd)/build/venv"; then - error "Failed to create a$MAGENTA Python$COLOR_RESET virtual environment. Delete the build/venv folder and try again." - - exit 1 - fi - - ok "Created a$MAGENTA Python$COLOR_RESET virtual environment (venv)." - - rm "$(pwd)/build/requirements.txt" > /dev/null 2>&1 || true -fi - - -# If the user wishes to enter the python venv -# mfc.sh must be source'd for this to work -if [ "$1" == "venv" ]; then - if [[ "$VIRTUAL_ENV" != "" ]]; then - # Already inside the venv, exit out of it - - log " > Exiting the$MAGENTA MFC Python$COLOR_RESET virtual environment." - - deactivate - else - # Enter the venv - log " > Entering the$MAGENTA MFC Python$COLOR_RESET virtual environment." - - source "$(pwd)/build/venv/bin/activate" - - log " > To exit, you can do any of the following:" - log " - Run 'deactivate'." - log " - Run '. ./mfc.sh venv'." - log " - Close your terminal." - fi - - return -fi - - -# Activate the Python venv -source "$(pwd)/build/venv/bin/activate" -ok "(venv) Entered the $MAGENTA$(python3 --version)$COLOR_RESET virtual environment (>= $MAGENTA$MFC_PYTHON_MIN_STR$COLOR_RESET)." - - -# Install Python dependencies if, either: -# - This script is running for the first time -# (or) -# - The requirements.txt file has changed -if ! cmp "$(pwd)/toolchain/requirements.txt" "$(pwd)/build/requirements.txt" > /dev/null 2>&1; then - log "(venv) (Re)Installing mfc.sh's Python dependencies (via Pip)." - - if ! PIP_DISABLE_PIP_VERSION_CHECK=1 pip3 install -r "$(pwd)/toolchain/requirements.txt"; then - error "(venv) Installation failed." - - log "(venv) Exiting the$MAGENTA Python$COLOR_RESET virtual environment." - deactivate - - exit 1 - fi - - ok "(venv) Installation succeeded." - - # Save the new/current requirements.txt - cp "$(pwd)/toolchain/requirements.txt" "$(pwd)/build/" -fi - +. "$(pwd)/toolchain/bootstrap/cmake.sh" +. "$(pwd)/toolchain/bootstrap/python.sh" # Run the main.py bootstrap script python3 "$(pwd)/toolchain/mfc.py" "$@" code=$? +if [ $code -ne 0 ]; then + error "mfc.py finished with a $code exit code." +fi # Deactivate the Python virtualenv in case the user "source"'d this script log "(venv) Exiting the$MAGENTA Python$COLOR_RESET virtual environment." deactivate - # Exit with proper exit code exit $code diff --git a/toolchain/bootstrap/cmake.sh b/toolchain/bootstrap/cmake.sh new file mode 100644 index 000000000..47437c934 --- /dev/null +++ b/toolchain/bootstrap/cmake.sh @@ -0,0 +1,113 @@ +#!/bin/bash + +# If we downloaded our own version of CMake, let us make it accessible to +# any command we run hereafter. + +if [ -f "$(pwd)/build/cmake/bin/cmake" ]; then + export PATH="$(pwd)/build/cmake/bin:$PATH" + + log "Found$MAGENTA CMake$COLOR_RESET: $MAGENTA$(pwd)/build/cmake/bin/cmake$COLOR_RESET." +fi + +bShouldInstallCMake=false +if ! command -v cmake > /dev/null 2>&1; then + # Not installed + bShouldInstallCMake=true + + warn "$MAGENTA""CMake$COLOR_RESET is not installed." +else + cmake_verstr=$(cmake --version | tr ' ' '\n' | sed -n 3p) + cmake_major=$(echo $cmake_verstr | tr '.' '\n' | sed -n 1p) + cmake_minor=$(echo $cmake_verstr | tr '.' '\n' | sed -n 2p) + cmake_patch=$(echo $cmake_verstr | tr '.' '\n' | sed -n 3p) + cmake_version="$(printf %05d%05d%05d $cmake_major $cmake_minor $cmake_patch)" + + MFC_CMAKE_MIN_VERSTR=$(cat CMakeLists.txt | grep cmake_minimum_required | head -n 1 | sed 's/[^0-9,.]*//g') + MFC_CMAKE_MIN_MAJOR=$(echo $MFC_CMAKE_MIN_VERSTR | tr '.' '\n' | head -n 1) + MFC_CMAKE_MIN_MINOR=$(echo $MFC_CMAKE_MIN_VERSTR | tr '.' '\n' | head -n 2 | tail -n 1) + MFC_CMAKE_MIN_PATCH=0 + MFC_CMAKE_MIN_VERSION="$(printf %05d%05d%05d $MFC_CMAKE_MIN_MAJOR $MFC_CMAKE_MIN_MINOR $MFC_CMAKE_MIN_PATCH)" + + if [ "$cmake_version" -lt "$MFC_CMAKE_MIN_VERSION" ]; then + # Out of date + bShouldInstallCMake=true + + log "$MAGENTA CMake$YELLOW is out of date (current: $MAGENTA$cmake_verstr$YELLOW < minimum: $MAGENTA$MFC_CMAKE_MIN_VERSTR$YELLOW).$COLOR_RESET" + fi +fi + +if [ "$bShouldInstallCMake" = true ]; then + version="3.25.2" + arch="$(uname -m)" + + bErrorDoSelfDownload=false + if [[ "$OSTYPE" == "darwin"* ]]; then + error "Cannot fetch$MAGENTA CMake$COLOR_RESET for$MAGENTA macOS$COLOR_RESET." + bErrorDoSelfDownload=true + fi + + if ! [[ "$arch" == "x86_64" || "$arch" == "aarch64" ]]; then + error "Cannot fetch$MAGENTA CMake$COLOR_RESET for architecture $MAGENTA$arch$COLOR_RESET." + bErrorDoSelfDownload=true + fi + + if [ "$bErrorDoSelfDownload" = true ]; then + log "Please install$MAGENTA CMake$COLOR_RESET manually:" + log " - via$MAGENTA Aptitude$COLOR_RESET: $ sudo apt install cmake" + log " - via$MAGENTA Homebrew$COLOR_RESET: $ brew install cmake" + log " - via$MAGENTA Spack$COLOR_RESET: $ spack install cmake" + log " - via$MAGENTA Pacman$COLOR_RESET: $ sudo pacman -S cmake" + log " - via$MAGENTA Lmod$COLOR_RESET: $ module load cmake (if available)" + log " - via$MAGENTA cmake.org$COLOR_RESET: https://cmake.org/download/" + + exit 1 + fi + + if [ -d "$(pwd)/build/cmake" ]; then + rm -rf "$(pwd)/build/cmake" + fi + + mkdir -p "$(pwd)/build/cmake" + + filename="cmake-$version-linux-$arch.sh" + repository="https://github.com/Kitware/CMake" + url="$repository/releases/download/v$version/$filename" + + log "Downloading$MAGENTA CMake v$version$COLOR_RESET for $MAGENTA$arch$COLOR_RESET from $CYAN$repository$COLOR_RESET." + + cmake_fatal_error() { + log "$MAGENTA""CMake$COLOR_RESET is not discoverable or is an older release, incompatible with$MAGENTA MFC$COLOR_RESET. Please download" + log "or install a recent version of$MAGENTA CMake$COLOR_RESET to get past this step. If you are currently on a" + log "managed system like a cluster, provided there is no suitable environment module, you can" + log "either build it from source, or get it via$MAGENTA Spack$COLOR_RESET." + log "- The minimum required version is currently$MAGENTA CMake$COLOR_RESET v$MFC_CMAKE_MIN_MAJOR.$MFC_CMAKE_MIN_MINOR.$MFC_CMAKE_MIN_PATCH." + log "- We attempted to download$MAGENTA CMake$COLOR_RESET v$version from $url." + + exit 1 + } + + if ! command -v wget > /dev/null 2>&1; then + error "$MAGENTA""wget$COLOR_RESET is not installed but is necessary to download$MAGENTA CMake$COLOR_RESET." + + cmake_fatal_error + fi + + if ! wget -P "$(pwd)/build/cmake" "$url"; then + error "Failed to download a compatible version of$MAGENTA CMake$COLOR_RESET." + + cmake_fatal_error + fi + + log "Installing$MAGENTA CMake$COLOR_RESET into $MAGENTA$(pwd)/build/cmake$COLOR_RESET." + + if ! $SHELL "$(pwd)/build/cmake/$filename" "--skip-license" "--prefix=$(pwd)/build/cmake"; then + error "Failed to install a compatible version of CMake." + + cmake_fatal_error + fi + + rm "$(pwd)/build/cmake/$filename" + + ok "Found$MAGENTA CMake$COLOR_RESET: $MAGENTA$(pwd)/build/cmake/bin/cmake$COLOR_RESET." + export PATH="$(pwd)/build/cmake/bin:$PATH" +fi diff --git a/toolchain/bootstrap/docker.sh b/toolchain/bootstrap/docker.sh new file mode 100644 index 000000000..73e61372d --- /dev/null +++ b/toolchain/bootstrap/docker.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +if ! command -v docker > /dev/null 2>&1; then + error "$MAGENTA""Docker$COLOR_RESET is not installed." + + exit 1 +fi + +log "Running in$MAGENTA Docker$COLOR_RESET mode." + +if [ -t 1 ]; then + dockerintopts='--interactive --tty' +fi + +__docker_run() { + docker run $dockerintopts --rm --workdir /home/me/MFC \ + --mount type=bind,source="$(pwd)",target=/home/me/MFC \ + sbryngelson/mfc:latest $@ +} + +__docker_run sudo chown -R me:me /home/me/MFC +if (($?)); then + error "Docker: Failed to set directory permissions on MFC mount.." + + exit 1 +fi + +__docker_run $@ +if (($?)); then + error "Error running Docker container with $@." + + exit 1 +fi \ No newline at end of file diff --git a/toolchain/bootstrap/format.sh b/toolchain/bootstrap/format.sh new file mode 100644 index 000000000..d6aea99de --- /dev/null +++ b/toolchain/bootstrap/format.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +log "Formatting MFC with fprettify..." + +fprettify ${@:-src} --exclude "src/*/autogen" --recursive --silent \ + --indent 4 --c-relations --enable-replacements --enable-decl \ + --whitespace-comma 1 --whitespace-multdiv 1 --whitespace-plusminus 1 \ + --case 1 1 1 1 --strict-indent +ret="$?" + +if [ "$ret" != '0' ]; then + error "failed to execute fprettify." + error "MFC has not been fprettify'ied." + + exit 1 +fi + +ok "MFC has been fprettify'ied." \ No newline at end of file diff --git a/toolchain/bootstrap/modules.sh b/toolchain/bootstrap/modules.sh new file mode 100644 index 000000000..7c4b47eef --- /dev/null +++ b/toolchain/bootstrap/modules.sh @@ -0,0 +1,130 @@ +#!/bin/bash + +unset u_c +unset u_cg + +# Reset u_computer & u_cg to known values since this script is run with "source" +# Therefore, values of variables defined here are kept when the script runs again. +u_computer=""; u_cg="" + +# If there are command-line arguments, parse them: +while [[ $# -gt 0 ]]; do + case $1 in + -c|--computer) u_c="$2"; shift; shift; ;; + -m|--mode) u_cg="$2"; shift; shift; ;; + -*|--*) echo "Unknown option $1"; return; ;; + esac +done + +# Get computer (if not supplied in command-line) +if [ -v $u_c ]; then + log "Select a system:" + log "$G""ORNL$W: Ascent (a) | Crusher (c) | Summit (s) | Wombat (w)" + log "$C""ACCESS$W: Bridges2 (b) | Expanse (e) | Delta (d)" + log "$Y""GaTech$W: Phoenix (p)" + log "$R""CALTECH$W: Richardson (r)" + log_n "($G""a$W/$G""c$W/$G""s$W/$G""w$W/$C""b$W/$C""e$CR/$C""d$CR/$Y""p$CR/$R""r$CR): " + read u_c + log +fi + +# Get CPU/GPU (if not supplied in command-line) +if [ -v $u_cg ]; then + log "Select configuration:" + log " - CPU (c | cpu)" + log " - GPU (g | gpu)" + log_n "(c/cpu/g/gpu): " + read u_cg + log +fi + +# User input to lowercase +u_c=$(echo "$u_c" | tr '[:upper:]' '[:lower:]') +u_cg=$(echo "$u_cg" | tr '[:upper:]' '[:lower:]') + +if [ "$u_cg" == 'c' ] || [ "$u_cg" == 'cpu' ]; then + CG='CPU'; cg='cpu' +elif [ "$u_cg" == "g" ] || [ "$u_cg" == 'gpu' ]; then + CG='GPU'; cg='gpu' +fi + +__combine() { + echo -n $@ | sed 's/\[\n\r\s\]\+/\ /' +} + +__extract() { + __combine "$(grep -E "^$1\s+" toolchain/modules | sed "s/^$1\s\+//")" +} + +COMPUTER="$(__extract "$u_c")" + +if [[ -z "$COMPUTER" ]]; then + error "Computer $M$u_cg$CR not recognized." + return +fi + +MODULES=($(__extract "$u_c-$cg") $(__combine $(__extract "$u_c-all"))) + +log "Loading modules for $M$COMPUTER$CR on $M$CG$CR"'s:' + +# Reset modules to default system configuration +if [ "$u_c" != 'p' ]; then + module reset > /dev/null 2>&1 + code="$?" + + # Purge if reset is not available + if [ "$code" -ne '0' ]; then + module purge > /dev/null 2>&1 + fi +else + module purge > /dev/null 2>&1 +fi + +# Find length of longest module_name in $MODULES for $COMPUTER +max_module_length="0" +for module_name in ${MODULES[@]}; do + module_length="${#module_name}" + + if [ "$module_length" -gt "$max_module_length" ]; then + max_module_length="$module_length" + fi +done + +# Load modules ($MODULES) +for module_name in ${MODULES[@]}; do + log_n " - $CYAN$module_name$COLOR_RESET " + + # Add padding spaces + module_length="${#module_name}" + delta="$((max_module_length-module_length-1))" + if [ "$delta" -ge "0" ]; then + printf "%0.s " $(seq 0 $delta) + fi + + # Load the module + module load "$module_name" > /dev/null 2>&1 + + # Handle Success / Failure + code=$? + if [ "$code" == '0' ]; then + echo -e "[$G""SUCCESS$W]" + else + echo -e "[$R""FAILURE$W]" + + # Run load again to show error message + module load "$module_name" + + return + fi +done + +if [ "$cg" == 'gpu' ]; then + isnv=$($FC --version | grep NVIDIA | wc -l) + if [ $isnv -eq 0 ]; then + export CC=nvc CXX=nvc++ FC=nvfortran + fi +fi +ok 'All modules and environment variables have been loaded.' + + +return diff --git a/toolchain/bootstrap/python.sh b/toolchain/bootstrap/python.sh new file mode 100644 index 000000000..7c31b840f --- /dev/null +++ b/toolchain/bootstrap/python.sh @@ -0,0 +1,131 @@ +#!/bin/bash + +MFC_PYTHON_MIN_MAJOR=3 +MFC_PYTHON_MIN_MINOR=8 +MFC_PYTHON_MIN_STR="$MFC_PYTHON_MIN_MAJOR.$MFC_PYTHON_MIN_MINOR" + +is_python_compatible() { + if ! ${1:-python3} -c "import sys; exit(int(not (sys.version_info[0]==$MFC_PYTHON_MIN_MAJOR and sys.version_info[1] >= $MFC_PYTHON_MIN_MINOR)))"; then + return 1 + fi + + return 0 +} + +assert_python_compatible() { + if ! is_python_compatible $1; then + error "$MAGENTA$(${1:-python3} --version)$COLOR_RESET (${1:-python3}) is out of date. Required >= $MAGENTA$MFC_PYTHON_MIN_STR$COLOR_RESET." + + exit 1 + fi +} + +if [ -f "$(pwd)/build/venv/bin/activate" ]; then + if ! is_python_compatible "$(pwd)/build/venv/bin/python3"; then + warn "$MAGENTA""Python$COLOR_RESET is outdated inside the Virtualenv." + warn "Deleting the Virtualenv and starting from scratch..." + + rm -r "$(pwd)/build/venv" + fi +fi + +if ! command -v pip3 > /dev/null 2>&1 && [ ! -f "$(pwd)/build/venv/bin/activate" ]; then + # Check whether python3 is in the $PATH / is accessible. + if ! command -v python3 > /dev/null 2>&1; then + error "Couldn't find$MAGENTA Python$COLOR_RESET. Please ensure it is discoverable." + + exit 1 + fi + + assert_python_compatible + + get_pip_url="https://bootstrap.pypa.io/pip/get-pip.py" + + warn "$MAGENTA""Python$COLOR_RESET's$MAGENTA PIP$COLOR_RESET is not installed." + log "Downloading$MAGENTA Python$COLOR_RESET's$MAGENTA PIP$COLOR_RESET from $get_pip_url..." + + if ! wget -O "$(pwd)/build/get-pip.py" "$get_pip_url"; then + error "Couldn't download get-pip.py." + + exit 1 + fi + + # Suppress PIP version warning (out of date) + export PIP_DISABLE_PIP_VERSION_CHECK=1 + if ! python3 "$(pwd)/build/get-pip.py" --user; then + error "Couldn't install$MAGENTA pip$COLOR_RESET with get-pip.py" + + exit 1 + fi + + ok "Installed pip." +fi + + +# Create a Python virtualenv if it hasn't already been created +if [ ! -f "$(pwd)/build/venv/bin/activate" ]; then + assert_python_compatible + + if ! python3 -m venv "$(pwd)/build/venv"; then + error "Failed to create a$MAGENTA Python$COLOR_RESET virtual environment. Delete the build/venv folder and try again." + + exit 1 + fi + + ok "Created a$MAGENTA Python$COLOR_RESET virtual environment (venv)." + + rm "$(pwd)/build/requirements.txt" > /dev/null 2>&1 || true +fi + + +# If the user wishes to enter the python venv +# mfc.sh must be source'd for this to work +if [ "$1" == "venv" ]; then + if [[ "$VIRTUAL_ENV" != "" ]]; then + # Already inside the venv, exit out of it + + log " > Exiting the$MAGENTA MFC Python$COLOR_RESET virtual environment." + + deactivate + else + # Enter the venv + log " > Entering the$MAGENTA MFC Python$COLOR_RESET virtual environment." + + source "$(pwd)/build/venv/bin/activate" + + log " > To exit, you can do any of the following:" + log " - Run 'deactivate'." + log " - Run '. ./mfc.sh venv'." + log " - Close your terminal." + fi + + return +fi + + +# Activate the Python venv +source "$(pwd)/build/venv/bin/activate" +ok "(venv) Entered the $MAGENTA$(python3 --version)$COLOR_RESET virtual environment (>= $MAGENTA$MFC_PYTHON_MIN_STR$COLOR_RESET)." + + +# Install Python dependencies if, either: +# - This script is running for the first time +# (or) +# - The requirements.txt file has changed +if ! cmp "$(pwd)/toolchain/requirements.txt" "$(pwd)/build/requirements.txt" > /dev/null 2>&1; then + log "(venv) (Re)Installing mfc.sh's Python dependencies (via Pip)." + + if ! PIP_DISABLE_PIP_VERSION_CHECK=1 pip3 install -r "$(pwd)/toolchain/requirements.txt"; then + error "(venv) Installation failed." + + log "(venv) Exiting the$MAGENTA Python$COLOR_RESET virtual environment." + deactivate + + exit 1 + fi + + ok "(venv) Installation succeeded." + + # Save the new/current requirements.txt + cp "$(pwd)/toolchain/requirements.txt" "$(pwd)/build/" +fi diff --git a/toolchain/modules b/toolchain/modules index b6fe4753c..39b5cd345 100644 --- a/toolchain/modules +++ b/toolchain/modules @@ -46,4 +46,3 @@ c-all cmake/3.23.2 cray-fftw/3.3.10.2 hdf5/1.12.1 cray-python/3.9.13.1 c-all ninja/1.10.2 cray-mpich/8.1.23 c-cpu c-gpu rocm/5.1.0 craype-accel-amd-gfx90a - diff --git a/toolchain/requirements.txt b/toolchain/requirements.txt index afee88b5a..f1be68d20 100644 --- a/toolchain/requirements.txt +++ b/toolchain/requirements.txt @@ -4,4 +4,5 @@ wheel typing PyYAML argparse +fprettify dataclasses