diff --git a/.github/workflows/modules/append_module.lua b/.github/workflows/modules/append_module.lua new file mode 100644 index 00000000..e45cb640 --- /dev/null +++ b/.github/workflows/modules/append_module.lua @@ -0,0 +1,3 @@ +setenv("INSIDE_GITHUB_ACTIONS", "true") +-- Interfere with PATH so Lmod keeps a record +prepend_path("PATH", "/snap/bin") diff --git a/.github/workflows/modules/prepend_module.lua b/.github/workflows/modules/prepend_module.lua new file mode 100644 index 00000000..e45cb640 --- /dev/null +++ b/.github/workflows/modules/prepend_module.lua @@ -0,0 +1,3 @@ +setenv("INSIDE_GITHUB_ACTIONS", "true") +-- Interfere with PATH so Lmod keeps a record +prepend_path("PATH", "/snap/bin") diff --git a/.github/workflows/scripts/test_init_scripts.sh b/.github/workflows/scripts/test_init_scripts.sh index f543835c..f2c8b03c 100755 --- a/.github/workflows/scripts/test_init_scripts.sh +++ b/.github/workflows/scripts/test_init_scripts.sh @@ -11,11 +11,17 @@ if [ -z ${EXPECTED_EASYBUILD_VERSION} ]; then exit 1 fi +if [ -z ${EESSI_SOFTWARE_SUBDIR_OVERRIDE} ]; then + echo "\$EESSI_SOFTWARE_SUBDIR_OVERRIDE has to be set (e.g., x86_64/intel/haswell) so we can do well defined string comparison for the architecture." + exit 1 +fi + # initialize assert framework if [ ! -d assert.sh ]; then echo "assert.sh not cloned." echo "" echo "run \`git clone https://github.com/lehmannro/assert.sh.git\`" + echo "(see workflow file that calls this script for how to only clone specific commit if you are worried about security)" exit 1 fi . assert.sh/assert.sh @@ -28,12 +34,16 @@ for shell in ${SHELLS[@]}; do echo RUNNING TESTS FOR SHELL: $shell echo = | awk 'NF += (OFS = $_) + 100' if [[ ! " ${TEST_SHELLS[*]} " =~ [[:space:]]${shell}[[:space:]] ]]; then - ### EXCEPTION FOR CSH ### echo -e "\033[33mWe don't now how to test the shell '$shell', PRs are Welcome.\033[0m" else + if [ "$shell" = "csh" ]; then + # make sure our .cshrc is empty before we begin as we will clobber it + [ -f ~/.cshrc ] && mv ~/.cshrc ~/.cshrc_orig + fi + # TEST 1: Source Script and check Module Output - expected="Module for EESSI/$EESSI_VERSION loaded successfully" - assert "$shell -c 'source init/lmod/$shell' 2>&1 " "${expected}" + expected_pattern=".*EESSI has selected $EESSI_SOFTWARE_SUBDIR_OVERRIDE as the compatible CPU target for EESSI/$EESSI_VERSION.*" + assert_raises "$shell -c 'source init/lmod/$shell' 2>&1 | grep -E \"${expected_pattern}\"" # TEST 2: Check if module overviews first section is the loaded EESSI module if [ "$shell" = "csh" ]; then @@ -43,21 +53,22 @@ for shell in ${SHELLS[@]}; do echo "source init/lmod/$shell" > ~/.cshrc MODULE_SECTIONS=($($shell -c "module ov" 2>&1 | grep -e '---')) else - MODULE_SECTIONS=($($shell -c "source init/lmod/$shell 2>/dev/null; module ov 2>&1 | grep -e '---'")) + MODULE_SECTIONS=($($shell -c "source init/lmod/$shell >/dev/null 2>&1; module ov 2>&1 | grep -e '---'")) fi - PATTERN="/cvmfs/software\.eessi\.io/versions/$EESSI_VERSION/software/linux/x86_64/(intel/haswell|amd/zen3)/modules/all" + PATTERN="/cvmfs/software\.eessi\.io/versions/$EESSI_VERSION/software/linux/$EESSI_SOFTWARE_SUBDIR_OVERRIDE/modules/all" assert_raises 'echo "${MODULE_SECTIONS[1]}" | grep -E "$PATTERN"' + # echo "${MODULE_SECTIONS[1]}" "$PATTERN" # TEST 3: Check if module overviews second section is the EESSI init module - assert "echo ${MODULE_SECTIONS[4]}" "/cvmfs/software.eessi.io/versions/$EESSI_VERSION/init/modules" + assert "echo ${MODULE_SECTIONS[4]}" "/cvmfs/software.eessi.io/init/modules" # TEST 4: Load EasyBuild module and check version # eb --version outputs: "This is EasyBuild 5.1.1 (framework: 5.1.1, easyblocks: 5.1.1) on host ..." if [ "$shell" = "csh" ]; then echo "source init/lmod/$shell" > ~/.cshrc - command="$shell -c 'module load EasyBuild/${EXPECTED_EASYBUILD_VERSION}; eb --version' | cut -d \" \" -f4" + command="$shell -c 'module load EasyBuild/${EXPECTED_EASYBUILD_VERSION}; eb --version' | tail -n 1 | awk '{print \$4}'" else - command="$shell -c 'source init/lmod/$shell 2>/dev/null; module load EasyBuild/${EXPECTED_EASYBUILD_VERSION}; eb --version | cut -d \" \" -f4'" + command="$shell -c 'source init/lmod/$shell >/dev/null 2>&1; module load EasyBuild/${EXPECTED_EASYBUILD_VERSION}; eb --version' | tail -n 1 | awk '{print \$4}'" fi assert "$command" "$EXPECTED_EASYBUILD_VERSION" @@ -69,12 +80,82 @@ for shell in ${SHELLS[@]}; do EASYBUILD_PATH=$($shell -c "source init/lmod/$shell 2>/dev/null; module load EasyBuild/${EXPECTED_EASYBUILD_VERSION}; which eb") fi # escape the dots in ${EASYBUILD_VERSION} - PATTERN="/cvmfs/software\.eessi\.io/versions/$EESSI_VERSION/software/linux/x86_64/(intel/haswell|amd/zen3)/software/EasyBuild/${EXPECTED_EASYBUILD_VERSION//./\\.}/bin/eb" - echo "$EASYBUILD_PATH" | grep -E "$PATTERN" + PATTERN="/cvmfs/software\.eessi\.io/versions/$EESSI_VERSION/software/linux/$EESSI_SOFTWARE_SUBDIR_OVERRIDE/software/EasyBuild/${EXPECTED_EASYBUILD_VERSION//./\\.}/bin/eb" + # echo "$EASYBUILD_PATH" | grep -E "$PATTERN" assert_raises 'echo "$EASYBUILD_PATH" | grep -E "$PATTERN"' + # echo "$EASYBUILD_PATH" "$PATTERN" + + # TEST 6 and 7: Check the various options (EESSI_DEFAULT_MODULES_APPEND, EESSI_DEFAULT_MODULES_APPEND, EESSI_EXTRA_MODULEPATH) all work + if [ "$shell" = "csh" ]; then + echo "setenv EESSI_DEFAULT_MODULES_APPEND append_module" > ~/.cshrc + echo "setenv EESSI_DEFAULT_MODULES_PREPEND prepend_module" >> ~/.cshrc + echo "setenv EESSI_EXTRA_MODULEPATH .github/workflows/modules" >> ~/.cshrc + echo "source init/lmod/$shell" >> ~/.cshrc + TEST_LMOD_SYSTEM_DEFAULT_MODULES=$($shell -c 'echo $LMOD_SYSTEM_DEFAULT_MODULES') + TEST_MODULEPATH=$($shell -c 'echo $MODULEPATH') + elif [ "$shell" = "fish" ]; then + TEST_LMOD_SYSTEM_DEFAULT_MODULES=$($shell -c 'set -x EESSI_DEFAULT_MODULES_APPEND append_module ; set -x EESSI_DEFAULT_MODULES_PREPEND prepend_module ; set -x EESSI_EXTRA_MODULEPATH .github/workflows/modules ; source init/lmod/'"$shell"' 2>/dev/null; echo $LMOD_SYSTEM_DEFAULT_MODULES') + TEST_MODULEPATH=$($shell -c 'set -x EESSI_DEFAULT_MODULES_APPEND append_module ; set -x EESSI_DEFAULT_MODULES_PREPEND prepend_module ; set -x EESSI_EXTRA_MODULEPATH .github/workflows/modules ; source init/lmod/'"$shell"' 2>/dev/null; echo $MODULEPATH') + else + TEST_LMOD_SYSTEM_DEFAULT_MODULES=$($shell -c 'export EESSI_DEFAULT_MODULES_APPEND=append_module ; export EESSI_DEFAULT_MODULES_PREPEND=prepend_module ; export EESSI_EXTRA_MODULEPATH=.github/workflows/modules ; source init/lmod/'"$shell"' ; echo $LMOD_SYSTEM_DEFAULT_MODULES') + TEST_MODULEPATH=$($shell -c 'export EESSI_DEFAULT_MODULES_APPEND=append_module ; export EESSI_DEFAULT_MODULES_PREPEND=prepend_module ; export EESSI_EXTRA_MODULEPATH=.github/workflows/modules ; source init/lmod/'"$shell"' 2>/dev/null; echo $MODULEPATH') + fi + LMOD_SYSTEM_DEFAULT_MODULES_PATTERN='^prepend_module:.*:append_module$' + # echo "$TEST_LMOD_SYSTEM_DEFAULT_MODULES" AND "$LMOD_SYSTEM_DEFAULT_MODULES_PATTERN" + assert_raises 'echo "$TEST_LMOD_SYSTEM_DEFAULT_MODULES" | grep -E "$LMOD_SYSTEM_DEFAULT_MODULES_PATTERN"' + if [ "$shell" = "fish" ]; then + MODULEPATH_PATTERN='\.github/workflows/modules$' + else + MODULEPATH_PATTERN=':\.github/workflows/modules$' + fi + # echo "$TEST_MODULEPATH" AND "$MODULEPATH_PATTERN" + assert_raises 'echo "$TEST_MODULEPATH" | grep -E "$MODULEPATH_PATTERN"' + + # TEST 8 and 9: Add a conditional test depending on whether we have the Lmod command is available locally or not (Ubuntu-based location for CI) + if [ -d "$LMOD_PKG/init" ]; then + echo "Running check for locally available Lmod with purge" + if [ "$shell" = "csh" ]; then + echo "source $LMOD_PKG/init/$shell" > ~/.cshrc + echo "source init/lmod/$shell" >> ~/.cshrc + TEST_EESSI_WITH_PURGE=$($shell -c 'echo') + echo "source $LMOD_PKG/init/$shell" > ~/.cshrc + echo "setenv EESSI_NO_MODULE_PURGE_ON_INIT 1" >> ~/.cshrc + echo "source init/lmod/$shell" >> ~/.cshrc + TEST_EESSI_WITHOUT_PURGE=$($shell -c 'echo $EESSI_NO_MODULE_PURGE_ON_INIT') + elif [ "$shell" = "fish" ]; then + TEST_EESSI_WITH_PURGE=$($shell -c "source $LMOD_PKG/init/$shell 2>/dev/null ; source init/lmod/$shell 2>/dev/null") + TEST_EESSI_WITHOUT_PURGE=$($shell -c "set -x EESSI_NO_MODULE_PURGE_ON_INIT 1 ; source $LMOD_PKG/init/$shell 2>/dev/null ; source init/lmod/$shell 2>/dev/null") + else + TEST_EESSI_WITH_PURGE=$($shell -c "source $LMOD_PKG/init/$shell 2>/dev/null ; source init/lmod/$shell 2>/dev/null") + TEST_EESSI_WITHOUT_PURGE=$($shell -c "export EESSI_NO_MODULE_PURGE_ON_INIT=1 ; source $LMOD_PKG/init/$shell 2>/dev/null ; source init/lmod/$shell 2>/dev/null") + fi + # In the first case we should have the test and in the second case we shouldn't + pattern="Modules purged before initialising EESSI" + echo $TEST_EESSI_WITH_PURGE + assert_raises 'echo "$TEST_EESSI_WITH_PURGE" | grep "$pattern"' + # this case should raise 1 + echo $TEST_EESSI_WITHOUT_PURGE + assert_raises 'echo "$TEST_EESSI_WITHOUT_PURGE" | grep "$pattern"' 1 + fi + + # Optional test 10, check if the prompt has been updated + if [ "$shell" = "bash" ] || [ "$shell" = "ksh" ] || [ "$shell" = "zsh" ]; then + # Typically this is a non-interactive shell, so manually unset PS1 and reset to a non-exported variable when testing + TEST_EESSI_PS1_UPDATE=$($shell -c "unset PS1 ; PS1='$ ' ; source init/lmod/$shell 2>/dev/null ; echo \"\$PS1\"") + TEST_EESSI_NO_PS1_UPDATE=$($shell -c "unset PS1 ; source init/lmod/$shell 2>/dev/null ; echo \"\$PS1\"") + pattern="{EESSI/${EESSI_VERSION}} " + assert_raises 'echo "$TEST_EESSI_PS1_UPDATE" | grep "$pattern"' + assert_raises 'echo "$TEST_EESSI_NO_PS1_UPDATE" | grep "$pattern"' 1 + fi # End Test Suite assert_end "source_eessi_$shell" + + if [ "$shell" = "csh" ]; then + # Restore our .cshrc + [ -f ~/.cshrc_orig ] && mv ~/.cshrc_orig ~/.cshrc + fi + fi done diff --git a/.github/workflows/tests_eessi_module.yml b/.github/workflows/tests_eessi_module.yml index e0a866ea..65ca858b 100644 --- a/.github/workflows/tests_eessi_module.yml +++ b/.github/workflows/tests_eessi_module.yml @@ -109,11 +109,11 @@ jobs: # First do (and undo) the Lmod initialisation export MODULEPATH=init/modules # Turn on debug output in case we want to take a look - export EESSI_DEBUG_INIT=true + export EESSI_MODULE_DEBUG_INIT=true CPU_ARCH=$(./init/eessi_archdetect.sh -a cpupath) module load EESSI/${{matrix.EESSI_VERSION}} - # EESSI_DEBUG_INIT/EESSI_ARCHDETECT_OPTIONS only relevant for Lmod init - unset EESSI_DEBUG_INIT + # EESSI_MODULE_DEBUG_INIT/EESSI_ARCHDETECT_OPTIONS only relevant for Lmod init + unset EESSI_MODULE_DEBUG_INIT # Store all relevant environment variables env | grep -E '(^EESSI_|^LMOD_RC|^LMOD_PACKAGE_PATH|^MODULEPATH)' | grep -v EESSI_ARCHDETECT_OPTIONS | sort > "${moduleoutfile}" module unload EESSI/${{matrix.EESSI_VERSION}} @@ -200,7 +200,7 @@ jobs: fi # Turn on debug output in case we want to take a look - export EESSI_DEBUG_INIT=true + export EESSI_MODULE_DEBUG_INIT=true initial_env_file="initial_env.txt" module_cycled_file="load_unload_cycle.txt" diff --git a/.github/workflows/tests_init_module.yml b/.github/workflows/tests_init_module.yml index b4d31435..902c31e3 100644 --- a/.github/workflows/tests_init_module.yml +++ b/.github/workflows/tests_init_module.yml @@ -46,8 +46,13 @@ jobs: sed -i "s/__EESSI_VERSION_DEFAULT__/${{matrix.EESSI_VERSION}}/g" init/lmod/${shell} done - - name: Clone assert.sh script - run: git clone https://github.com/lehmannro/assert.sh.git + - name: Clone assert.sh at pinned commit + run: | + ASSERT_COMMIT_SHA=fe359e341670f1e8e86a3804ca00e5a3ebc30fa4 + git clone --no-checkout https://github.com/lehmannro/assert.sh.git + cd assert.sh + git fetch --depth 1 origin $ASSERT_COMMIT_SHA + git checkout $ASSERT_COMMIT_SHA - name: Install missing shells run: | @@ -55,9 +60,20 @@ jobs: sudo apt install zsh ksh fish tcsh echo "# INIT ZSH" > ~/.zshrc - - name: Run tests for available shells + - name: Run tests for available shells without system Lmod run: | + export EESSI_SOFTWARE_SUBDIR_OVERRIDE=${{matrix.EESSI_SOFTWARE_SUBDIR_OVERRIDE}} + export EESSI_VERSION=${{matrix.EESSI_VERSION}} + export EXPECTED_EASYBUILD_VERSION=${{matrix.EXPECTED_EASYBUILD_VERSION}} + .github/workflows/scripts/test_init_scripts.sh "bash" "zsh" "ksh" "fish" "csh" + - name: Run tests for available shells with system Lmod + run: | + # We also want to perform the same test when there is an Lmod version available on the system + sudo apt install lmod + source /usr/share/lmod/lmod/init/bash + export MODULEPATH=/usr/share/lmod/lmod/modulefiles + module avail + export EESSI_SOFTWARE_SUBDIR_OVERRIDE=${{matrix.EESSI_SOFTWARE_SUBDIR_OVERRIDE}} export EESSI_VERSION=${{matrix.EESSI_VERSION}} export EXPECTED_EASYBUILD_VERSION=${{matrix.EXPECTED_EASYBUILD_VERSION}} .github/workflows/scripts/test_init_scripts.sh "bash" "zsh" "ksh" "fish" "csh" - diff --git a/init/lmod/bash b/init/lmod/bash index daba8351..c8154bd9 100644 --- a/init/lmod/bash +++ b/init/lmod/bash @@ -1,19 +1,117 @@ +#!/usr/bin/env bash + +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (C) 2020-2026 EESSI contributors +# +# EESSI - European Environment for Scientific Software Installations +# +# This file is a template for initialising EESSI via Lmod for the environment indicated by the shebang. +# +# Assumptions: +# - EESSI accessible under $EESSI_CVMFS_REPO or /cvmfs/software.eessi.io +# - expected to be true as this file is also meant to be shipped under that location +# - Lmod must the shipped in the repository in the correct location +# +# Options: +# - The script listens to environment variables so that it can be silently configured by a site +# - EESSI_CVMFS_REPO: perform the initialisation from a respository other than /cvmfs/software.eessi.io +# - EESSI_VERSION: loads a specific EESSI version ignoring the default in the file +# - EESSI_DEFAULT_MODULES_PREPEND: environment variable allows you to prepend modules to the defaults (loaded last, : separated) +# - EESSI_DEFAULT_MODULES_APPEND: environment variable allows you to append modules to the defaults (loaded first, : separated) +# - EESSI_EXTRA_MODULEPATH: environment variable allows a site to append to MODULEPATH (: separated, lower priority than EESSI MODULEPATH) +# - EESSI_NO_MODULE_PURGE_ON_INIT: environment variable that disables purging modules before initialisation +# +# Other options that can be set to influence the end result: +# - The EESSI module also listens to environment variables so that it can be silently configured by a site +# - EESSI_MODULE_FAMILY_NAME: use the value of the environment variable to set an Lmod family for the EESSI module +# - EESSI_MODULE_STICKY: make the EESSI module sticky +# - EESSI_MODULE_UPDATE_PS1: have the EESSI module update PS1 to give a prompt that is prepended with "{EESSI/...} " (requires exporting PS1) +# (- EESSI_MODULE_DEBUG_INIT: enable debug print statements when loading the EESSI module) +# +# Effects: +# - Should always succeed +# - Initialises Lmod from specific version of EESSI +# - Clobbers any existing Lmod configuration +# - Some special environment variables that are internal to Lmod (__LMOD_REF_COUNT_MODULEPATH and +# _ModuleTable001_) are used to force a hard reset of an existing Lmod installation. This +# approach may be brittle. +# - Then loads module EESSI/... to initialise EESSI +# - use `module show EESSI/...` to see the environment variables set by the EESSI module +# +# Reverting the effects: +# - EESSI initialisation via `module unload EESSI/...` +# - Lmod initialisation cannot be undone + # Choose an EESSI CVMFS repository EESSI_CVMFS_REPO="${EESSI_CVMFS_REPO:-/cvmfs/software.eessi.io}" -# Choose an EESSI version -EESSI_VERSION_DEFAULT="__EESSI_VERSION_DEFAULT__" + +# Choose an EESSI version (the default is only used if the EESSI_VERSION environment variable is not provided) +# (Note: in the repository which is home to this file a template value __EESSI_VERSION_DEFAULT__ is present in +# the line below which is replaced within our deployment pipeline.) +EESSI_VERSION_DEFAULT="${__EESSI_VERSION_USED_FOR_INIT:-__EESSI_VERSION_DEFAULT__}" EESSI_VERSION="${EESSI_VERSION:-${EESSI_VERSION_DEFAULT}}" -# Path to top-level module tree -export MODULEPATH="${EESSI_CVMFS_REPO}/versions/${EESSI_VERSION}/init/modules" -. "${EESSI_CVMFS_REPO}/versions/${EESSI_VERSION}/compat/linux/$(uname -m)/usr/share/Lmod/init/bash" +# On the first run we want to record the EESSI version used for init as an environment variable so that if a different +# version of this script is called (e.g, for a a different EESSI version) it retains a memory which EESSI +# version was actually used in the initialisation. This is useful as __Init_EESSI_Default_Modules used below will +# be defined on the first call and Lmod initialisation will not happen twice. +# This sets the value only on first execution, if the variable already exists in the environment +# the original value is retained. +export __EESSI_VERSION_USED_FOR_INIT="${__EESSI_VERSION_USED_FOR_INIT:-${EESSI_VERSION}}" + +# LMOD_SYSTEM_DEFAULT_MODULES are used by Lmod to load a default set of modules in the scenario +# - where we initialise Lmod (the if part of the clause below) +# - where we reset Lmod (the else part of the clause below) +# This means that if we call this script twice we will get the same end result: an Lmod installation +# with a set of default modules loaded. +# +# We also allow the ability to predefine elsewhere the default list of modules to load (colon separated): +# - EESSI_DEFAULT_MODULES_PREPEND environment variable allows you to prepend modules (loaded last) +# - EESSI_DEFAULT_MODULES_APPEND environment variable allows you to append modules (loaded first) +LMOD_SYSTEM_DEFAULT_MODULES="${EESSI_DEFAULT_MODULES_PREPEND:+$EESSI_DEFAULT_MODULES_PREPEND:}EESSI/$EESSI_VERSION${EESSI_DEFAULT_MODULES_APPEND:+:$EESSI_DEFAULT_MODULES_APPEND}" +export LMOD_SYSTEM_DEFAULT_MODULES + +if [ -z "$__Init_EESSI_Default_Modules" ]; then + export __Init_EESSI_Default_Modules=1; + + # Lmod version in 2023.06 has a problem with newer Lmod caches, so let's stick to more recent Lmod + # (has no effect except on Lmod itself, and compatible caches are still created/supported by EESSI) + LMOD_EESSI_VERSION=${EESSI_VERSION/2023.06/2025.06} + + # Let's attempt a purge of any loaded modules as any environment variables currently set will survive EESSI initialisation + # (it's ok if the module command does not exist) + if [ -z "$EESSI_NO_MODULE_PURGE_ON_INIT" ]; then + module purge >/dev/null 2>&1 && echo "Modules purged before initialising EESSI" + fi + + # Lmod may have been initialised so we need to clear some internal variables to allow for a full reset + # - make it forget about the system set MODULEPATH + unset __LMOD_REF_COUNT_MODULEPATH + # - and clear out any memory Lmod might have + unset _ModuleTable001_ + + # For the shells that use PS1 for the prompt, let's add the trigger to enable updating that by default + # (in an interactive shell PS1 is likely unset, so let's only do this if it is set) + if [ -n "$PS1" ]; then + export PS1 + export EESSI_MODULE_UPDATE_PS1=1 + fi -if [ -z "$__Init_Default_Modules" ]; then - export __Init_Default_Modules=1; + # Path to top-level module tree + # - EESSI_EXTRA_MODULEPATH environment variable allows a site to append to MODULEPATH (lower priority than EESSI MODULEPATH) + export MODULEPATH="${EESSI_CVMFS_REPO}/init/modules${EESSI_EXTRA_MODULEPATH:+:$EESSI_EXTRA_MODULEPATH}" + . "${EESSI_CVMFS_REPO}/versions/${LMOD_EESSI_VERSION}/compat/linux/$(uname -m)/usr/share/Lmod/init/bash" + module --initial_load --no_redirect restore - ## ability to predefine elsewhere the default list - LMOD_SYSTEM_DEFAULT_MODULES=${LMOD_SYSTEM_DEFAULT_MODULES:-"EESSI/$EESSI_VERSION"} - export LMOD_SYSTEM_DEFAULT_MODULES - module --initial_load --no_redirect restore + # After initialising, we now know the architecture(s) that was/were selected so let's report them + echo "EESSI has selected ${EESSI_SOFTWARE_SUBDIR} as the compatible CPU target for EESSI/${EESSI_VERSION}" + if [ -n "$EESSI_ACCEL_SUBDIR" ]; then + echo "EESSI has selected ${EESSI_ACCEL_SUBDIR} as the compatible accelerator target for EESSI/${EESSI_VERSION}" + else + echo "EESSI did not identify an accelerator on the system" + fi + # If people want more detailed information about what EESSI is doing, they can also set an environment variable + # for additional information. + echo "(for debug information when loading the EESSI module, set the environment variable EESSI_MODULE_DEBUG_INIT)" else - module refresh + module reset fi diff --git a/init/lmod/csh b/init/lmod/csh index 2adb314c..1270a3f4 100644 --- a/init/lmod/csh +++ b/init/lmod/csh @@ -1,24 +1,87 @@ +#!/usr/bin/env csh + +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (C) 2020-2026 EESSI contributors +# +# EESSI - European Environment for Scientific Software Installations +# +# This file is a template for initialising EESSI via Lmod for the environment indicated by the shebang. +# +# Please refer to the reference bash implementation for full documentation, only minimal comments are included here + # Choose an EESSI CVMFS repository -if (! $?EESSI_CVMFS_REPO) then +if ( ! $?EESSI_CVMFS_REPO) then set EESSI_CVMFS_REPO = "/cvmfs/software.eessi.io" endif -# Choose an EESSI version -setenv EESSI_VERSION_DEFAULT "__EESSI_VERSION_DEFAULT__" -if (! $?EESSI_VERSION) then - set EESSI_VERSION = "${EESSI_VERSION_DEFAULT}" + +# Choose an EESSI version (default is used if EESSI_VERSION is not provided) +if ( ! $?__EESSI_VERSION_USED_FOR_INIT ) then + set EESSI_VERSION_DEFAULT = "__EESSI_VERSION_DEFAULT__" +else + set EESSI_VERSION_DEFAULT = "$__EESSI_VERSION_USED_FOR_INIT" +endif +if ( ! $?EESSI_VERSION ) then + set EESSI_VERSION = "$EESSI_VERSION_DEFAULT" +endif +# On first run, record the EESSI version used for init as an environment variable. +# We use setenv to ensure it is available to child processes (equivalent to export). +if ( ! $?__EESSI_VERSION_USED_FOR_INIT ) then + setenv __EESSI_VERSION_USED_FOR_INIT "$EESSI_VERSION" +endif + +# ability to predefine elsewhere the default list (with options to append or prepend) +set LMOD_SYSTEM_DEFAULT_MODULES = "EESSI/${EESSI_VERSION}" +if ( $?EESSI_DEFAULT_MODULES_PREPEND ) then + set LMOD_SYSTEM_DEFAULT_MODULES = "${EESSI_DEFAULT_MODULES_PREPEND}:${LMOD_SYSTEM_DEFAULT_MODULES}" +endif +if ( $?EESSI_DEFAULT_MODULES_APPEND ) then + set LMOD_SYSTEM_DEFAULT_MODULES = "${LMOD_SYSTEM_DEFAULT_MODULES}:${EESSI_DEFAULT_MODULES_APPEND}" endif -# Path to top-level module tree -setenv MODULEPATH "${EESSI_CVMFS_REPO}/versions/${EESSI_VERSION}/init/modules" -source "${EESSI_CVMFS_REPO}/versions/${EESSI_VERSION}/compat/linux/`uname -m`/usr/share/Lmod/init/csh" +setenv LMOD_SYSTEM_DEFAULT_MODULES "${LMOD_SYSTEM_DEFAULT_MODULES}" + +if ( ! $?__Init_EESSI_Default_Modules ) then + setenv __Init_EESSI_Default_Modules 1 + + # Lmod version in 2023.06 has a problem with newer Lmod caches, so let's stick to more recent Lmod + # (has no effect except on Lmod itself, and compatible caches are still created/supported by EESSI) + set LMOD_EESSI_VERSION = "${EESSI_VERSION}" + if ( "${LMOD_EESSI_VERSION}" == "2023.06" ) then + set LMOD_EESSI_VERSION = "2025.06" + endif -if (! $?__Init_Default_Modules ) then - setenv __Init_Default_Modules 1 + if ( ! $?EESSI_NO_MODULE_PURGE_ON_INIT ) then + module purge >& /dev/null + if ( $status == 0 ) echo "Modules purged before initialising EESSI" + endif - # ability to predefine elsewhere the default list - if (! $?LMOD_SYSTEM_DEFAULT_MODULES) then - setenv LMOD_SYSTEM_DEFAULT_MODULES "EESSI/${EESSI_VERSION}" + # If there is a local Lmod, make it forget about the system set MODULEPATH + unsetenv __LMOD_REF_COUNT_MODULEPATH + # and clear out any memory Lmod might have + unsetenv _ModuleTable001_ + # Path to top-level module tree + set modulepath = "${EESSI_CVMFS_REPO}/init/modules" + if ( $?EESSI_EXTRA_MODULEPATH ) then + # Now that we know it exists, check IF it is not empty + if ( "$EESSI_EXTRA_MODULEPATH" != "" ) then + set modulepath = "${modulepath}:${EESSI_EXTRA_MODULEPATH}" + endif endif + setenv MODULEPATH "$modulepath" + source "${EESSI_CVMFS_REPO}/versions/${LMOD_EESSI_VERSION}/compat/linux/`uname -m`/usr/share/Lmod/init/csh" + module --initial_load --no_redirect restore + + echo "EESSI has selected ${EESSI_SOFTWARE_SUBDIR} as the compatible CPU target for EESSI/${EESSI_VERSION}" + if ( $?EESSI_ACCEL_SUBDIR ) then + if ( "$EESSI_ACCEL_SUBDIR" != "" ) then + echo "EESSI has selected ${EESSI_ACCEL_SUBDIR} as the compatible accelerator target for EESSI/${EESSI_VERSION}" + else + echo "EESSI did not identify an accelerator on the system" + endif + else + echo "EESSI did not identify an accelerator on the system" + endif + echo "(for debug information when loading the EESSI module, set the environment variable EESSI_MODULE_DEBUG_INIT)" else - module refresh + module reset endif diff --git a/init/lmod/fish b/init/lmod/fish index 9e595441..60d0beb7 100644 --- a/init/lmod/fish +++ b/init/lmod/fish @@ -1,18 +1,77 @@ +#!/usr/bin/env fish + +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (C) 2020-2026 EESSI contributors +# +# EESSI - European Environment for Scientific Software Installations +# +# This file is a template for initialising EESSI via Lmod for the environment indicated by the shebang. +# +# Please refer to the reference bash implementation for full documentation, only minimal comments are included here + # Choose an EESSI CVMFS repository set EESSI_CVMFS_REPO (set -q EESSI_CVMFS_REPO; and echo "$EESSI_CVMFS_REPO"; or echo "/cvmfs/software.eessi.io") + # Choose an EESSI version -set EESSI_VERSION_DEFAULT "__EESSI_VERSION_DEFAULT__" -set EESSI_VERSION (set -q EESSI_VERSION; and echo "$EESSI_VERSION"; or echo "$EESSI_VERSION_DEFAULT") -# Path to top-level module tree -set -x MODULEPATH "$EESSI_CVMFS_REPO"/versions/"$EESSI_VERSION"/init/modules -. "$EESSI_CVMFS_REPO"/versions/"$EESSI_VERSION"/compat/linux/(uname -m)/usr/share/Lmod/init/fish - -if test -z "$__Init_Default_Modules" - export __Init_Default_Modules=1; - - ## ability to predefine elsewhere the default list - set -x LMOD_SYSTEM_DEFAULT_MODULES (set -q LMOD_SYSTEM_DEFAULT_MODULE; and echo "$LMOD_SYSTEM_DEFAULT_MODULE"; or echo "EESSI/$EESSI_VERSION") - module --initial_load --no_redirect restore +if not set -q __EESSI_VERSION_USED_FOR_INIT + set EESSI_VERSION_DEFAULT "__EESSI_VERSION_DEFAULT__" +else + set EESSI_VERSION_DEFAULT "$__EESSI_VERSION_USED_FOR_INIT" +end +if not set -q EESSI_VERSION + set EESSI_VERSION "$EESSI_VERSION_DEFAULT" +end + +# Record version used for init; -x exports it to the environment +if not set -q __EESSI_VERSION_USED_FOR_INIT + set -x __EESSI_VERSION_USED_FOR_INIT "$EESSI_VERSION" +end + +# ability to predefine elsewhere the default list (with options to append or prepend) +set LMOD_SYSTEM_DEFAULT_MODULES "EESSI/$EESSI_VERSION" +if set -q EESSI_DEFAULT_MODULES_PREPEND + set LMOD_SYSTEM_DEFAULT_MODULES "$EESSI_DEFAULT_MODULES_PREPEND:$LMOD_SYSTEM_DEFAULT_MODULES" +end +if set -q EESSI_DEFAULT_MODULES_APPEND + set LMOD_SYSTEM_DEFAULT_MODULES "$LMOD_SYSTEM_DEFAULT_MODULES:$EESSI_DEFAULT_MODULES_APPEND" +end +set -x LMOD_SYSTEM_DEFAULT_MODULES $LMOD_SYSTEM_DEFAULT_MODULES + +if test -z "$__Init_EESSI_Default_Modules" + set -x __Init_EESSI_Default_Modules 1 + + # Lmod version in 2023.06 has a problem with newer Lmod caches, so let's stick to more recent Lmod + # (has no effect except on Lmod itself, and compatible caches are still created/supported by EESSI) + set LMOD_EESSI_VERSION (string replace 2023.06 2025.06 -- $EESSI_VERSION) + + if test -z "$EESSI_NO_MODULE_PURGE_ON_INIT" + if type -q module + module purge >/dev/null 2>&1 + and echo "Modules purged before initialising EESSI" + end + end + + # If there is a local Lmod, make it forget about the system set MODULEPATH + set -e __LMOD_REF_COUNT_MODULEPATH + # and clear out any memory Lmod might have + set -e _ModuleTable001_ + # Path to top-level module tree + set modulepath "$EESSI_CVMFS_REPO/init/modules" + if set -q EESSI_EXTRA_MODULEPATH; and test -n "$EESSI_EXTRA_MODULEPATH" + set modulepath "$modulepath:$EESSI_EXTRA_MODULEPATH" + end + set -x MODULEPATH $modulepath + . "$EESSI_CVMFS_REPO"/versions/"$LMOD_EESSI_VERSION"/compat/linux/(uname -m)/usr/share/Lmod/init/fish + + module --initial_load --no_redirect restore + + echo "EESSI has selected $EESSI_SOFTWARE_SUBDIR as the compatible CPU target for EESSI/$EESSI_VERSION" + if test -n "$EESSI_ACCEL_SUBDIR" + echo "EESSI has selected $EESSI_ACCEL_SUBDIR as the compatible accelerator target for EESSI/$EESSI_VERSION" + else + echo "EESSI did not identify an accelerator on the system" + end + echo "(for debug information when loading the EESSI module, set the environment variable EESSI_MODULE_DEBUG to 1)" else - module refresh + module reset end diff --git a/init/lmod/ksh b/init/lmod/ksh index ebf4c0ca..72ff8203 100644 --- a/init/lmod/ksh +++ b/init/lmod/ksh @@ -1,19 +1,73 @@ +#!/usr/bin/env ksh + +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (C) 2020-2026 EESSI contributors +# +# EESSI - European Environment for Scientific Software Installations +# +# This file is a template for initialising EESSI via Lmod for the environment indicated by the shebang. +# +# Please refer to the reference bash implementation for full documentation, only minimal comments are included here + # Choose an EESSI CVMFS repository EESSI_CVMFS_REPO="${EESSI_CVMFS_REPO:-/cvmfs/software.eessi.io}" -# Choose an EESSI version -EESSI_VERSION_DEFAULT="__EESSI_VERSION_DEFAULT__" + +# Choose an EESSI version (the default is only used if the EESSI_VERSION environment variable is not provided) +EESSI_VERSION_DEFAULT="${__EESSI_VERSION_USED_FOR_INIT:-__EESSI_VERSION_DEFAULT__}" EESSI_VERSION="${EESSI_VERSION:-${EESSI_VERSION_DEFAULT}}" -# Path to top-level module tree -export MODULEPATH="${EESSI_CVMFS_REPO}/versions/${EESSI_VERSION}/init/modules" -. "${EESSI_CVMFS_REPO}/versions/${EESSI_VERSION}/compat/linux/$(uname -m)/usr/share/Lmod/init/ksh" +# On the first run we want to record the EESSI version used for init as an environment variable so that if a different +# version of this script is called (e.g, for a a different EESSI version) it retains a memory which EESSI +# version was actually used in the initialisation. This is useful as __Init_EESSI_Default_Modules used below will +# be defined on the first call and Lmod initialisation will not happen twice. +# This sets the value only on first execution, if the variable already exists in the environment +# the original value is retained. +export __EESSI_VERSION_USED_FOR_INIT="${__EESSI_VERSION_USED_FOR_INIT:-${EESSI_VERSION}}" + +# ability to predefine elsewhere the default list (with options to append or prepend) +LMOD_SYSTEM_DEFAULT_MODULES="${EESSI_DEFAULT_MODULES_PREPEND:+$EESSI_DEFAULT_MODULES_PREPEND:}EESSI/$EESSI_VERSION${EESSI_DEFAULT_MODULES_APPEND:+:$EESSI_DEFAULT_MODULES_APPEND}" +export LMOD_SYSTEM_DEFAULT_MODULES + +if [ -z "$__Init_EESSI_Default_Modules" ]; then + export __Init_EESSI_Default_Modules=1; + + # Lmod version in 2023.06 has a problem with newer Lmod caches, so let's stick to more recent Lmod + # (has no effect except on Lmod itself, and compatible caches are still created/supported by EESSI) + LMOD_EESSI_VERSION=${EESSI_VERSION/2023.06/2025.06} + + # Let's attempt a purge of any loaded modules as any environment variables currently set will survive EESSI initialisation + # (it's ok if the module command does not exist) + if [ -z "$EESSI_NO_MODULE_PURGE_ON_INIT" ]; then + module purge >/dev/null 2>&1 && echo "Modules purged before initialising EESSI" + fi + + # If there is a local Lmod, make it forget about the system set MODULEPATH + unset __LMOD_REF_COUNT_MODULEPATH + # and clear out any memory Lmod might have + unset _ModuleTable001_ + + # For the shells that use PS1 for the prompt, let's add the trigger to enable updating that by default + # (in an interactive shell PS1 is likely unset, so let's only do this if it is set) + if [ -n "$PS1" ]; then + export PS1 + export EESSI_MODULE_UPDATE_PS1=1 + fi + + # Path to top-level module tree + export MODULEPATH="${EESSI_CVMFS_REPO}/init/modules${EESSI_EXTRA_MODULEPATH:+:$EESSI_EXTRA_MODULEPATH}" + . "${EESSI_CVMFS_REPO}/versions/${LMOD_EESSI_VERSION}/compat/linux/$(uname -m)/usr/share/Lmod/init/ksh" -if [ -z "$__Init_Default_Modules" ]; then - export __Init_Default_Modules=1; + module --initial_load --no_redirect restore - ## ability to predefine elsewhere the default list - LMOD_SYSTEM_DEFAULT_MODULES=${LMOD_SYSTEM_DEFAULT_MODULES:-"EESSI/$EESSI_VERSION"} - export LMOD_SYSTEM_DEFAULT_MODULES - module --initial_load --no_redirect restore + # After initialising, we now know the architecture(s) that was/were selected so let's report them + echo "EESSI has selected ${EESSI_SOFTWARE_SUBDIR} as the compatible CPU target for EESSI/${EESSI_VERSION}" + if [ -n "$EESSI_ACCEL_SUBDIR" ]; then + echo "EESSI has selected ${EESSI_ACCEL_SUBDIR} as the compatible accelerator target for EESSI/${EESSI_VERSION}" + else + echo "EESSI did not identify an accelerator on the system" + fi + # If people want more detailed information about what EESSI is doing, they can also set an environment variable + # for additional information. + echo "(for debug information when loading the EESSI module, set the environment variable EESSI_MODULE_DEBUG_INIT)" else - module refresh + module reset fi diff --git a/init/lmod/zsh b/init/lmod/zsh index dfdff0c7..d2f603d2 100644 --- a/init/lmod/zsh +++ b/init/lmod/zsh @@ -1,19 +1,73 @@ +#!/usr/bin/env zsh + +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (C) 2020-2026 EESSI contributors +# +# EESSI - European Environment for Scientific Software Installations +# +# This file is a template for initialising EESSI via Lmod for the environment indicated by the shebang. +# +# Please refer to the reference bash implementation for full documentation, only minimal comments are included here + # Choose an EESSI CVMFS repository EESSI_CVMFS_REPO="${EESSI_CVMFS_REPO:-/cvmfs/software.eessi.io}" -# Choose an EESSI version -EESSI_VERSION_DEFAULT="__EESSI_VERSION_DEFAULT__" + +# Choose an EESSI version (the default is only used if the EESSI_VERSION environment variable is not provided) +EESSI_VERSION_DEFAULT="${__EESSI_VERSION_USED_FOR_INIT:-__EESSI_VERSION_DEFAULT__}" EESSI_VERSION="${EESSI_VERSION:-${EESSI_VERSION_DEFAULT}}" -# Path to top-level module tree -export MODULEPATH="${EESSI_CVMFS_REPO}/versions/${EESSI_VERSION}/init/modules" -. "${EESSI_CVMFS_REPO}/versions/${EESSI_VERSION}/compat/linux/$(uname -m)/usr/share/Lmod/init/zsh" +# On the first run we want to record the EESSI version used for init as an environment variable so that if a different +# version of this script is called (e.g, for a a different EESSI version) it retains a memory which EESSI +# version was actually used in the initialisation. This is useful as __Init_EESSI_Default_Modules used below will +# be defined on the first call and Lmod initialisation will not happen twice. +# This sets the value only on first execution, if the variable already exists in the environment +# the original value is retained. +export __EESSI_VERSION_USED_FOR_INIT="${__EESSI_VERSION_USED_FOR_INIT:-${EESSI_VERSION}}" + +# ability to predefine elsewhere the default list (with options to append or prepend) +LMOD_SYSTEM_DEFAULT_MODULES="${EESSI_DEFAULT_MODULES_PREPEND:+$EESSI_DEFAULT_MODULES_PREPEND:}EESSI/$EESSI_VERSION${EESSI_DEFAULT_MODULES_APPEND:+:$EESSI_DEFAULT_MODULES_APPEND}" +export LMOD_SYSTEM_DEFAULT_MODULES + +if [ -z "$__Init_EESSI_Default_Modules" ]; then + export __Init_EESSI_Default_Modules=1; + + # Lmod version in 2023.06 has a problem with newer Lmod caches, so let's stick to more recent Lmod + # (has no effect except on Lmod itself, and compatible caches are still created/supported by EESSI) + LMOD_EESSI_VERSION=${EESSI_VERSION/2023.06/2025.06} + + # Let's attempt a purge of any loaded modules as any environment variables currently set will survive EESSI initialisation + # (it's ok if the module command does not exist) + if [ -z "$EESSI_NO_MODULE_PURGE_ON_INIT" ]; then + module purge >/dev/null 2>&1 && echo "Modules purged before initialising EESSI" + fi + + # If there is a local Lmod, make it forget about the system set MODULEPATH + unset __LMOD_REF_COUNT_MODULEPATH + # and clear out any memory Lmod might have + unset _ModuleTable001_ + + # For the shells that use PS1 for the prompt, let's add the trigger to enable updating that by default + # (in an interactive shell PS1 is likely unset, so let's only do this if it is set) + if [ -n "$PS1" ]; then + export PS1 + export EESSI_MODULE_UPDATE_PS1=1 + fi + + # Path to top-level module tree + export MODULEPATH="${EESSI_CVMFS_REPO}/init/modules${EESSI_EXTRA_MODULEPATH:+:$EESSI_EXTRA_MODULEPATH}" + . "${EESSI_CVMFS_REPO}/versions/${LMOD_EESSI_VERSION}/compat/linux/$(uname -m)/usr/share/Lmod/init/zsh" -if [ -z "$__Init_Default_Modules" ]; then - export __Init_Default_Modules=1; + module --initial_load --no_redirect restore - ## ability to predefine elsewhere the default list - LMOD_SYSTEM_DEFAULT_MODULES=${LMOD_SYSTEM_DEFAULT_MODULES:-"EESSI/$EESSI_VERSION"} - export LMOD_SYSTEM_DEFAULT_MODULES - module --initial_load --no_redirect restore + # After initialising, we now know the architecture(s) that was/were selected so let's report them + echo "EESSI has selected ${EESSI_SOFTWARE_SUBDIR} as the compatible CPU target for EESSI/${EESSI_VERSION}" + if [ -n "$EESSI_ACCEL_SUBDIR" ]; then + echo "EESSI has selected ${EESSI_ACCEL_SUBDIR} as the compatible accelerator target for EESSI/${EESSI_VERSION}" + else + echo "EESSI did not identify an accelerator on the system" + fi + # If people want more detailed information about what EESSI is doing, they can also set an environment variable + # for additional information. + echo "(for debug information when loading the EESSI module, set the environment variable EESSI_MODULE_DEBUG_INIT)" else - module refresh + module reset fi diff --git a/init/modules/EESSI/2023.06.lua b/init/modules/EESSI/2023.06.lua index fd27749c..f408e90c 100644 --- a/init/modules/EESSI/2023.06.lua +++ b/init/modules/EESSI/2023.06.lua @@ -57,7 +57,8 @@ setenv("EESSI_VERSION", eessi_version) setenv("EESSI_CVMFS_REPO", eessi_repo) setenv("EESSI_OS_TYPE", eessi_os_type) function eessiDebug(text) - if (mode() == "load" and os.getenv("EESSI_DEBUG_INIT")) then + -- Allow the old environment or the new one (EESSI_MODULE_...) to enable the debug print statements + if (mode() == "load" and (os.getenv("EESSI_DEBUG_INIT") or os.getenv("EESSI_MODULE_DEBUG_INIT"))) then LmodMessage(text) end end