mirror of
https://github.com/Pmodules/Pmodules.git
synced 2026-06-24 00:27:59 +02:00
modulecmd: support for Lmod module files added
This commit is contained in:
+167
-158
@@ -37,7 +37,10 @@ declare -rx TCL_LIBRARY="${PMODULES_HOME}/lib/tcl@TCL_VERSION@"
|
||||
declare -x TCLLIBPATH
|
||||
std::prepend_path TCLLIBPATH "${PMODULES_HOME}/lib/Pmodules"
|
||||
|
||||
declare -r modulecmd="${libexecdir}/modulecmd.bin"
|
||||
declare -r tcl_cmd="${libexecdir}/modulecmd.bin"
|
||||
#declare -r lmod_cmd="${PMODULES_HOME}/lmod/lmod/libexec/lmod"
|
||||
declare -r lmod_cmd="/usr/share/lmod/lmod/libexec/lmod"
|
||||
declare -- modulecmd="${tcl_cmd}"
|
||||
|
||||
declare -- verbosity_lvl=${PMODULES_VERBOSITY:-'verbose'}
|
||||
|
||||
@@ -83,14 +86,14 @@ export_env() {
|
||||
# append-path/remove-path! But we keep the state in PMODULES_ENV
|
||||
# so we don't have to export it.
|
||||
#
|
||||
case "${Shell}" in
|
||||
sh | bash | zsh )
|
||||
echo "unset UsedGroups; "
|
||||
;;
|
||||
csh | tcsh )
|
||||
echo "unsetenv UsedGroups; "
|
||||
;;
|
||||
esac
|
||||
#case "${Shell}" in
|
||||
#sh | bash | zsh )
|
||||
# echo "unset UsedGroups; "
|
||||
# ;;
|
||||
#csh | tcsh )
|
||||
# echo "unsetenv UsedGroups; "
|
||||
# ;;
|
||||
#esac
|
||||
}
|
||||
|
||||
#
|
||||
@@ -121,7 +124,7 @@ save_env() {
|
||||
|
||||
[[ $1 == 'no' ]] && return 0
|
||||
local vars=( Version )
|
||||
vars+=( UsedReleaseStages UsedFlags UsedGroups )
|
||||
vars+=( UsedReleaseStages UsedGroups )
|
||||
vars+=( DefaultGroups DefaultReleaseStages )
|
||||
vars+=( ReleaseStages )
|
||||
vars+=( GroupDepths )
|
||||
@@ -165,6 +168,11 @@ die_wrong_number_of_args(){
|
||||
"${CMD}" "${subcommand}" "wrong number of arguments"
|
||||
}
|
||||
|
||||
die_illegal_opt(){
|
||||
std::die 3 "%s %s: %s -- %s\n" \
|
||||
"${CMD}" "${subcommand}" "illegal option" "$1"
|
||||
}
|
||||
|
||||
die_illegal_arg(){
|
||||
std::die 3 "%s %s: %s -- %s\n" \
|
||||
"${CMD}" "${subcommand}" "invalid argument" "$1"
|
||||
@@ -225,6 +233,12 @@ die_overlay_already_in_use(){
|
||||
"overlay already in use" "$1"
|
||||
}
|
||||
|
||||
die_not_a_modulefile(){
|
||||
std::die 3 "%s %s: %s -- %s" \
|
||||
"${CMD}" "${subcommand}" \
|
||||
"not a modulefile" "$1"
|
||||
}
|
||||
|
||||
#
|
||||
# get release stage of module
|
||||
# Note:
|
||||
@@ -238,12 +252,12 @@ die_overlay_already_in_use(){
|
||||
# $3 module name/version
|
||||
#
|
||||
get_release_stage() {
|
||||
local "$1"
|
||||
local -n grs_rel_stage="$1"
|
||||
local -r dir="$2"
|
||||
local -r modulefile="${dir}/$3"
|
||||
|
||||
if [[ ! -v Dir2OverlayMap[${dir%/${PMODULES_MODULEFILES_DIR}*}] ]]; then
|
||||
std::upvar $1 'stable'
|
||||
grs_rel_stage='stable'
|
||||
return
|
||||
fi
|
||||
#
|
||||
@@ -255,9 +269,9 @@ get_release_stage() {
|
||||
local -r rel_stage_file="${modulefile%/*}/.release-${modulefile##*/}"
|
||||
if [[ -r ${rel_stage_file} ]]; then
|
||||
# read file, remove empty lines, spaces etc
|
||||
std::upvar $1 $( < "${rel_stage_file}" )
|
||||
grs_rel_stage=$( < "${rel_stage_file}" )
|
||||
else
|
||||
std::upvar $1 'unstable'
|
||||
grs_rel_stage='unstable'
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -270,28 +284,25 @@ is_release_stage() {
|
||||
|
||||
#
|
||||
# Check whether a given moduledir is in an used overlay.
|
||||
# If yes, return 0 and the overlay with upvar of first argument
|
||||
# otherwise return 1
|
||||
# If yes, return 0 otherwise return 1
|
||||
#
|
||||
# Arguments
|
||||
# $1 upvar to return overlay
|
||||
# $2 upvar to return group
|
||||
# $1 ref.var to return overlay
|
||||
# $2 ref.var to return group
|
||||
# $3 moduledir to check
|
||||
#
|
||||
find_overlay () {
|
||||
local "$1"
|
||||
local "$2"
|
||||
local -n fo_ol="$1"
|
||||
local -n fo_group="$2"
|
||||
local path="${3//+(\/)/\/}" # replace multpile '/' with one
|
||||
path="${path/%\/}" # remove trailing slash if exist
|
||||
path="${path%/${PMODULES_MODULEFILES_DIR}*}"
|
||||
|
||||
# return if not in an overlay
|
||||
[[ -v Dir2OverlayMap[${path}] ]] || return 1
|
||||
|
||||
local ol="${Dir2OverlayMap[${path}]}"
|
||||
std::upvar $1 "${ol}"
|
||||
|
||||
local group="${path#${OverlayInfo[${ol}:mod_root]}/}"
|
||||
std::upvar $2 "${group}"
|
||||
fo_ol="${Dir2OverlayMap[${path}]}"
|
||||
fo_group="${path#${OverlayInfo[${ol}:mod_root]}/}"
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -303,17 +314,41 @@ module_is_loaded() {
|
||||
}
|
||||
|
||||
#
|
||||
# Check shebang.
|
||||
# test whether the given file is a module file.
|
||||
#
|
||||
# - for lua: the extension must be .lua
|
||||
# - for Tcl: check shebang
|
||||
#
|
||||
# return interpreter in reference variable
|
||||
#
|
||||
# Arguments:
|
||||
# $1 file name to test
|
||||
# $1 [out] ref. variable for result
|
||||
# $2 [in] full qualified file name to test
|
||||
#
|
||||
# Return value:
|
||||
# 0 if file exist, is readable and is either a lua or Tcl module file
|
||||
# 1 if file exist but is neither a lua or Tcl module file
|
||||
# 2 if file doesn't exist
|
||||
#
|
||||
is_modulefile() {
|
||||
local -r fname="$1"
|
||||
local -n im_interp="$1"
|
||||
local -r fname="$2"
|
||||
|
||||
# does file exist and is readable?
|
||||
[[ -r ${fname} ]] || return 2
|
||||
|
||||
if [[ "${fname##*.}" == 'lua' ]]; then
|
||||
im_interp='lmod'
|
||||
return 0
|
||||
fi
|
||||
local -- shebang
|
||||
[[ -r ${fname} ]] || return 1
|
||||
read -n 11 shebang < "${fname}"
|
||||
[[ "${shebang:0:8}" == '#%Module' ]] || [[ "${shebang:0:9}" == '#%Pmodule' ]]
|
||||
if [[ "${shebang:0:8}" == '#%Module' ]] \
|
||||
|| [[ "${shebang:0:9}" == '#%Pmodule' ]]; then
|
||||
im_interp='tcl'
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
#
|
||||
@@ -437,59 +472,6 @@ subcommand_load() {
|
||||
local prefix=''
|
||||
local m=''
|
||||
|
||||
#......................................................................
|
||||
# Test whether a given module is available.
|
||||
# The passed module-name can be
|
||||
#
|
||||
# - an absolute file- or link-name.
|
||||
# The module can be either in- or outside our hierarchy.
|
||||
#
|
||||
# - a relative file- or link-name.
|
||||
# The module can be either in- or outside out hierarchy.
|
||||
#
|
||||
# - specified with name and version (like gcc/5.2.0).
|
||||
# The module can be either in- or outside our hierarchy.
|
||||
#
|
||||
# - specified with name only (without version, like gcc).
|
||||
# The module can be either in- or outside our hierarchy.
|
||||
#
|
||||
# - directory in- or outsite our hierarchy (not supported by
|
||||
# modulecmd.tcl!)
|
||||
#
|
||||
# arguments:
|
||||
# $1: module name or file
|
||||
#
|
||||
# possible return values:
|
||||
# 0: module is loadable
|
||||
# 1: either not a modulefile or unsused release stage
|
||||
#
|
||||
# The following variables in the enclosing function are set:
|
||||
# current_modulefile
|
||||
# prefix
|
||||
# rel_stage
|
||||
#
|
||||
is_available() {
|
||||
local m=$1
|
||||
local -a array
|
||||
#
|
||||
# the next command assigns the absolute modulefile path
|
||||
# to ${arry[0]} and - if the module is in our root - the
|
||||
# prefix of the module to ${array[1]}.
|
||||
#
|
||||
# The trick with the first line matching "_PREFIX" is not
|
||||
# 100% reliable: One of the Pmodules extensions must be
|
||||
# called before something like
|
||||
# setenv FOO_PREFIX bar
|
||||
# can be used.
|
||||
#
|
||||
mapfile -t array < <("${modulecmd}" 'bash' show "$m" 2>&1 | \
|
||||
${awk} 'NR == 2 {print substr($0, 1, length($0)-1)}; /_PREFIX |_HOME / {print $3; exit}')
|
||||
current_modulefile="${array[0]}"
|
||||
prefix="${array[1]}"
|
||||
test -n "${current_modulefile}" || return 1
|
||||
get_release_stage rel_stage "${current_modulefile}" "${UsedReleaseStages}"
|
||||
}
|
||||
|
||||
#......................................................................
|
||||
# output load 'hints'
|
||||
#
|
||||
@@ -618,9 +600,7 @@ subcommand_load() {
|
||||
(( ${#args[@]} == 0 )) && \
|
||||
die_missing_arg
|
||||
|
||||
IFS=':'
|
||||
local -a modulepath=(${MODULEPATH})
|
||||
unset IFS
|
||||
IFS=':' read -r -a modulepath <<< "${MODULEPATH}"
|
||||
|
||||
local m=''
|
||||
for m in "${args[@]}"; do
|
||||
@@ -633,6 +613,7 @@ subcommand_load() {
|
||||
# - rel_stage:group:name or
|
||||
# - name:rel_stage
|
||||
|
||||
|
||||
IFS=':'
|
||||
local -a toks=($m)
|
||||
unset IFS
|
||||
@@ -667,7 +648,6 @@ subcommand_load() {
|
||||
local -i depth=${GroupDepths[${group}]}
|
||||
(( depth != 0 )) && \
|
||||
die_illegal_group "${group}"
|
||||
modulepath=()
|
||||
group+="/${PMODULES_MODULEFILES_DIR}"
|
||||
for overlay in "${UsedOverlays[@]}"; do
|
||||
local mod_root="${OverlayInfo[${overlay}:mod_root]}"
|
||||
@@ -681,7 +661,8 @@ subcommand_load() {
|
||||
g_env_must_be_saved='yes'
|
||||
fi
|
||||
fi # handle extended module names
|
||||
find_module current_modulefile rel_stage "${m}" "${modulepath[@]}"
|
||||
local moduledir=''
|
||||
find_modulefile current_modulefile rel_stage moduledir "${m}" "${modulepath[@]}"
|
||||
if [[ -z ${current_modulefile} ]]; then
|
||||
local hints=''
|
||||
get_load_hints hints
|
||||
@@ -696,6 +677,9 @@ subcommand_load() {
|
||||
# continue if already loaded
|
||||
[[ ":${LOADEDMODULES}:" =~ ":${m}:" ]] && continue
|
||||
|
||||
local interp=''
|
||||
is_modulefile interp "${current_modulefile}" || \
|
||||
die_not_a_modulefile "${m}"
|
||||
local prefix=''
|
||||
get_module_prefix prefix "${current_modulefile}"
|
||||
if [[ -n ${prefix} ]]; then
|
||||
@@ -703,6 +687,12 @@ subcommand_load() {
|
||||
test -r "${prefix}/.dependencies" && load_dependencies "$_"
|
||||
fi
|
||||
|
||||
if [[ "${interp}" == 'lmod' ]]; then
|
||||
current_modulefile="${current_modulefile/${moduledir}\/}"
|
||||
modulecmd="${lmod_cmd}"
|
||||
else
|
||||
modulecmd="${tcl_cmd}"
|
||||
fi
|
||||
local output=$("${modulecmd}" 'bash' ${opts} 'load' \
|
||||
"${current_modulefile}" 2> "${tmpfile}")
|
||||
|
||||
@@ -794,6 +784,10 @@ subcommand_unload() {
|
||||
args+=( "$@" )
|
||||
break
|
||||
;;
|
||||
-* )
|
||||
die_illegal_opt "$1"
|
||||
;;
|
||||
|
||||
* )
|
||||
args+=( "$1" )
|
||||
;;
|
||||
@@ -810,9 +804,28 @@ subcommand_unload() {
|
||||
# with 'Pmodules', we save the value and set it at the end of
|
||||
# the loop again, if it has been unset.
|
||||
local saved_home="${PMODULES_HOME}"
|
||||
|
||||
|
||||
IFS=: read -r -a _lmfiles_ <<< "${_LMFILES_}"
|
||||
local arg
|
||||
local lmfile
|
||||
for arg in "${args[@]}"; do
|
||||
for lmfile in "${_lmfiles_[@]}" '_zzzz_'; do
|
||||
if [[ $lmfile =~ ${arg} ]]; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [[ "${lmfile}" == '_zzzz_' ]]; then
|
||||
continue
|
||||
fi
|
||||
local interp
|
||||
is_modulefile interp "${lmfile}" || die_not_a_modulefile "${m}"
|
||||
if [[ "${interp}" == 'lmod' ]]; then
|
||||
current_modulefile="${current_modulefile/${moduledir}\/}"
|
||||
modulecmd="${lmod_cmd}"
|
||||
else
|
||||
modulecmd="${tcl_cmd}"
|
||||
fi
|
||||
|
||||
local output=$("${modulecmd}" "${Shell}" 'unload' "${arg}")
|
||||
eval "$(echo "${output}"|${sed} -e 's/;unalias [^;]*//g')"
|
||||
case ${Shell} in
|
||||
@@ -825,7 +838,7 @@ subcommand_unload() {
|
||||
esac
|
||||
done
|
||||
if [[ -z ${PMODULES_HOME} ]]; then
|
||||
PMODULES_HOME=${saved_home}
|
||||
PMODULES_HOME="${saved_home}"
|
||||
export_env 'PMODULES_HOME'
|
||||
fi
|
||||
g_env_must_be_saved='yes'
|
||||
@@ -1017,10 +1030,10 @@ get_available_modules() {
|
||||
|
||||
#
|
||||
# find module(file) to load. Input arguments are
|
||||
# $1 upvar: return module file
|
||||
# $2 upvar: return module release stage
|
||||
# $3 module to load
|
||||
# $4 a modulepath (usually MODULEPATH)
|
||||
# $1 [out] ref. variable to return module file
|
||||
# $2 [out] ref. variable to return release stage
|
||||
# $3 |in] module to load
|
||||
# $4 [in] module search path (usually splitted MODULEPATH)
|
||||
#
|
||||
# The module name can be
|
||||
# name
|
||||
@@ -1028,18 +1041,17 @@ get_available_modules() {
|
||||
# name/version_flag
|
||||
#
|
||||
# Return
|
||||
# return code 0 and modulefile in first argument
|
||||
# return code 1 and modulefile in first argument
|
||||
# if module
|
||||
# return code 2
|
||||
# if no modulefile could be found
|
||||
# 0 if a modulefile has been found
|
||||
# != else
|
||||
#
|
||||
find_module() {
|
||||
local "$1"
|
||||
local "$2"
|
||||
local -r module="$3"
|
||||
local -a dirs=("${@:4}")
|
||||
find_modulefile() {
|
||||
local -n fm_modulefile="$1"
|
||||
local -n fm_rel_stage="$2"
|
||||
local -n fm_dir="$3"
|
||||
local -r module="$4"
|
||||
local -a dirs=("${@:5}")
|
||||
|
||||
local dir
|
||||
for dir in "${dirs[@]}"; do
|
||||
test -d "${dir}" || continue
|
||||
local -i col=$((${#dir} + 2 ))
|
||||
@@ -1052,20 +1064,21 @@ find_module() {
|
||||
-ipath "${dir}/${module}*" \
|
||||
| cut -b${col}-) )
|
||||
for mod in "${modules[@]}"; do
|
||||
#
|
||||
# loop over all used flags. If a module with
|
||||
# a used flag is available load this module.
|
||||
local rel_stage
|
||||
for flag in "${UseFlags[@]/#/_}" ""; do
|
||||
[[ ${mod} == ${module}${flag} ]] || continue
|
||||
std::upvar $1 "${dir}/${mod}"
|
||||
get_release_stage \
|
||||
rel_stage \
|
||||
"${dir}" \
|
||||
"${mod}"
|
||||
std::upvar $2 "${rel_stage}"
|
||||
return 0
|
||||
done
|
||||
if [[ ${mod} == ${module} ]]; then
|
||||
:
|
||||
elif [[ ${mod} == ${module}.lua ]]; then
|
||||
mod="${module}.lua"
|
||||
else
|
||||
continue
|
||||
fi
|
||||
get_release_stage \
|
||||
fm_rel_stage \
|
||||
"${dir}" \
|
||||
"${mod}"
|
||||
|
||||
fm_modulefile="${dir}/${mod}"
|
||||
fm_dir="${dir}"
|
||||
return 0
|
||||
done
|
||||
else
|
||||
# no version has been specified. This makes it more
|
||||
@@ -1084,26 +1097,26 @@ find_module() {
|
||||
# now modules contains a reverse sorted list of
|
||||
# available modules in the form name/version
|
||||
for mod in "${modules[@]}"; do
|
||||
#
|
||||
# loop over all used flags. If a module with
|
||||
# a used flag is available load this module.
|
||||
local rel_stage=''
|
||||
for flag in "${UseFlags[@]/#/_}" ""; do
|
||||
[[ ${mod} == ${module}/*${flag} ]] || continue
|
||||
std::upvar $1 "${dir}/${mod}"
|
||||
get_release_stage \
|
||||
rel_stage \
|
||||
"${dir}" \
|
||||
"${mod}"
|
||||
std::upvar $2 "${rel_stage}"
|
||||
[[ :${rel_stage}: =~ :${UsedReleaseStages}: ]] && \
|
||||
return 0
|
||||
done
|
||||
if [[ ${mod} == ${module}/* ]]; then
|
||||
:
|
||||
else
|
||||
continue
|
||||
fi
|
||||
get_release_stage \
|
||||
fm_rel_stage \
|
||||
"${dir}" \
|
||||
"${mod}"
|
||||
# don't load modules with unused release stages
|
||||
if [[ :${fm_rel_stage}: =~ :${UsedReleaseStages}: ]]; then
|
||||
fm_modulefile="${dir}/${mod}"
|
||||
fm_dir="${dir}"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
} # find_module()
|
||||
} # find_modulefile()
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
@@ -1310,24 +1323,40 @@ subcommand_avail() {
|
||||
unset IFS
|
||||
local dir
|
||||
local group=''
|
||||
local groups=()
|
||||
local ol=''
|
||||
local name=''
|
||||
for dir in "${modulepath[@]}"; do
|
||||
group='other'
|
||||
find_overlay ol group "${dir}"
|
||||
if (( $? == 0 )); then
|
||||
name="${group}"
|
||||
else
|
||||
name="${dir}"
|
||||
group="${dir//[\/ .-]/_}"
|
||||
fi
|
||||
if [[ ! -v modulepath_${group} ]]; then
|
||||
typeset -a modulepath_${group}
|
||||
fi
|
||||
# with overlays we can have multiple directories per group!
|
||||
typeset -n path=modulepath_${group}
|
||||
path+=("${dir}")
|
||||
local -i found=0
|
||||
for group in "${groups[@]}"; do
|
||||
if [[ "${group}" == ${name} ]]; then
|
||||
found=1
|
||||
fi
|
||||
done
|
||||
(( found == 0 )) && groups+=( "${name}" )
|
||||
done
|
||||
local p
|
||||
for string in "${pattern[@]}"; do
|
||||
for group in ${UsedGroups//:/ } other; do
|
||||
for group in "${groups[@]}"; do
|
||||
if (( ${#opt_groups[@]} > 0 )) && [[ ! -v opt_groups[${group}] ]]; then
|
||||
continue
|
||||
fi
|
||||
[[ -v modulepath_${group} ]] || continue
|
||||
typeset -n path=modulepath_${group}
|
||||
local ref="${group//[\/ .-]/_}"
|
||||
[[ -v modulepath_${ref} ]] || continue
|
||||
typeset -n path=modulepath_${ref}
|
||||
get_available_modules \
|
||||
mods \
|
||||
"${string}*" \
|
||||
@@ -1434,11 +1463,6 @@ subcommand_use() {
|
||||
[[ ! ":${UsedReleaseStages}:" =~ :$r: ]] && std::info "\t${r}"
|
||||
done
|
||||
|
||||
std::info "\nUsed flags:"
|
||||
for flag in "${UsedFlags[@]}"; do
|
||||
std::info "\t${flag}"
|
||||
done
|
||||
|
||||
std::info ''
|
||||
std::info "Used overlays:"
|
||||
print_ol_info 'yes'
|
||||
@@ -1534,11 +1558,6 @@ subcommand_use() {
|
||||
std::append_path UsedReleaseStages "${arg}"
|
||||
return $?
|
||||
fi
|
||||
if [[ "${arg}" =~ "flag=" ]]; then
|
||||
# argument is flag
|
||||
UsedFlags+=( "${arg/flag=}" )
|
||||
return $?
|
||||
fi
|
||||
if [[ -v OverlayInfo[${arg}:type] ]]; then
|
||||
use_overlay "${arg}"
|
||||
return $?
|
||||
@@ -1706,15 +1725,6 @@ subcommand_unuse() {
|
||||
std::remove_path UsedReleaseStages "${arg}"
|
||||
return
|
||||
fi
|
||||
if [[ "${arg}" =~ "flag=" ]]; then
|
||||
# argument is flag
|
||||
local flag="${arg/flag=}"
|
||||
local i
|
||||
for i in ${!UsedFlags[@]}; do
|
||||
[[ ${UsedFlags[i]} == ${flag} ]] && unset UsedFlags[i]
|
||||
done
|
||||
return
|
||||
fi
|
||||
if [[ -v OverlayInfo[${arg}:type] ]]; then
|
||||
unuse_overlay "${arg}"
|
||||
return 0
|
||||
@@ -1868,7 +1878,6 @@ pmodules_init() {
|
||||
declare -gx LOADEDMODULES=''
|
||||
declare -gx _LMFILES_=''
|
||||
declare -Ag GroupDepths=()
|
||||
declare -ag UsedFlags=()
|
||||
declare -g Version="${PMODULES_VERSION}"
|
||||
|
||||
init_used_groups
|
||||
|
||||
Reference in New Issue
Block a user