mirror of
https://github.com/Pmodules/Pmodules.git
synced 2026-06-22 07:47:57 +02:00
c72b203e5f
Due to a bug in this function not all available modules were found.
4042 lines
113 KiB
Plaintext
4042 lines
113 KiB
Plaintext
#!@BASH@ --noprofile
|
|
#
|
|
# shellcheck -x -e SC1008,SC2239,SC2317,SC2034,SC2128,SC2059,SC2178
|
|
#
|
|
declare -r VERSION='@PMODULES_VERSION@'
|
|
|
|
unset CDPATH # unset CDPATH, otherwise 'cd' prints the directoy!
|
|
unset IFS # use default IFS
|
|
|
|
#set -o errexit
|
|
set -o pipefail
|
|
set -o nounset
|
|
#shopt -s nocaseglob
|
|
#shopt -s extglob
|
|
shopt -s nullglob
|
|
|
|
# get absolute path of script
|
|
declare -- mydir=$(cd "$(/usr/bin/dirname -- "${BASH_SOURCE[0]}")" && pwd -L)
|
|
declare -- PMODULES_HOME=$(/usr/bin/dirname -- "${mydir}");
|
|
|
|
declare -- path_orig="${PATH}"
|
|
PATH="${PMODULES_HOME}/bin:${PMODULES_HOME}/libexec:/bin:/usr/bin:/sbin:/usr/sbin"
|
|
source "${PMODULES_HOME}/lib/libstd.bash" || {
|
|
echo "Oops: cannot source library -- '$_'" 1>&2; exit 3;
|
|
}
|
|
source "${PMODULES_HOME}/lib/libpmodules.bash" || \
|
|
std::die 3 "Oops: cannot source library -- '$_'"
|
|
|
|
declare -rx TCL_LIBRARY="${PMODULES_HOME}/lib/tcl@TCL_VERSION@"
|
|
declare -x TCLLIBPATH=${TCLLIBPATH:-''}
|
|
std::prepend_path TCLLIBPATH "${PMODULES_HOME}/lib/Pmodules"
|
|
declare -r Tcl_cmd="${PMODULES_HOME}/libexec/modulecmd.bin"
|
|
declare -r Lmod_cmd="${PMODULES_HOME}/libexec/lmod/lmod/libexec/lmod"
|
|
declare -r Spider_cmd="${PMODULES_HOME}/libexec/lmod/lmod/libexec/spider"
|
|
declare -- modulecmd="${Tcl_cmd}"
|
|
|
|
# we have to use the original path. Otherwise module load doesn't work.
|
|
PATH="${path_orig}"
|
|
unset path_orig
|
|
unset mydir
|
|
|
|
##############################################################################
|
|
# the following settings are used if the config file doesn't exist
|
|
|
|
# set groups which should be available after initialization
|
|
declare -- DefaultGroups='Tools Programming'
|
|
|
|
# define available release stages
|
|
declare -- ReleaseStages=':unstable:stable:deprecated:'
|
|
|
|
# set releases which should be available after initialization
|
|
declare -- DefaultReleaseStages='stable'
|
|
|
|
declare -A Dir2OverlayMap=()
|
|
declare -A GroupDepths=()
|
|
declare -- ModulePathAppend=''
|
|
declare -- ModulePathPrepend=''
|
|
declare -a Overlays=()
|
|
declare -- OverlayExcludes=''
|
|
declare -A OverlayInfo=()
|
|
declare -- PmFiles=''
|
|
declare -g UsedGroups=''
|
|
declare -g UsedReleaseStages=''
|
|
declare -a UsedOverlays=()
|
|
declare -- Version=''
|
|
declare -x OSRelease=$(std::get_os_release)
|
|
declare -x SystemCPU=$(uname -p)
|
|
declare -A MaskedGroups=()
|
|
|
|
##############################################################################
|
|
declare -- Verbosity_lvl='verbose'
|
|
declare -- Shell=''
|
|
TmpFile=$( ${mktemp} /tmp/Pmodules.XXXXXX ) || \
|
|
std::die 1 "Oops: unable to create tmp file!"
|
|
declare -r TmpFile
|
|
declare -r CacheDir="${HOME}/.cache/Pmodules"
|
|
${mkdir} -p "${CacheDir}" || \
|
|
std::die 1 "Oops: unable to create cache directoy '${CacheDir}"
|
|
declare -- SpiderCache=''
|
|
|
|
HostName=$(${hostname} -f)
|
|
declare -r HostName
|
|
|
|
CurTime=$( ${date} --date=now +%s )
|
|
declare -r CurTime
|
|
|
|
declare -r CMD='module'
|
|
declare -- SubCommand=''
|
|
declare -A Subcommands=()
|
|
declare -A Options=()
|
|
declare -A Help=()
|
|
|
|
# these variables must exist. Under some conditions Lmod unset them.
|
|
if [[ ! -v LOADEDMODULES ]] || [[ ! -v _LMFILES_ ]]; then
|
|
LOADEDMODULES=''
|
|
_LMFILES_=''
|
|
fi
|
|
[[ -v MODULEPATH ]] || MODULEPATH=''
|
|
[[ -v PMODULES_ENV ]] || PMODULES_ENV=''
|
|
[[ -v MANPATH ]] || MANPATH=''
|
|
|
|
# initialize help text of 'module --version'
|
|
Help['version']="
|
|
Pmodules @PMODULES_VERSION@
|
|
using Tcl Environment Modules
|
|
VERSION = @MODULES_VERSION@
|
|
"
|
|
|
|
#..............................................................................
|
|
print_help() {
|
|
local -r __doc__='Display help text for command given in $1.'
|
|
|
|
echo -e "${Help[$1]}" 1>&2
|
|
std::die 1
|
|
}
|
|
|
|
#..............................................................................
|
|
save_env() {
|
|
local -r __doc__='
|
|
Save/cache state in the environment variable PMODULES_ENV.
|
|
The content is base64 encoded. This function is called on exit
|
|
via a trap handler.'
|
|
encode_base64(){
|
|
local -- os_name=''
|
|
os_name=$(${uname} -s)
|
|
|
|
case "${os_name}" in
|
|
Linux )
|
|
"${base64}" --wrap=0 <<< "$1"
|
|
;;
|
|
Darwin )
|
|
# does not wrap if running in a script
|
|
"${base64}" <<< "$1"
|
|
;;
|
|
* )
|
|
std::die 255 "Oops: Unsupported OS"
|
|
;;
|
|
esac
|
|
}
|
|
|
|
local vars=()
|
|
vars+=( 'Version' )
|
|
vars+=( 'UsedReleaseStages' 'UsedGroups' )
|
|
vars+=( 'DefaultGroups' 'DefaultReleaseStages' )
|
|
vars+=( 'ReleaseStages' )
|
|
vars+=( 'GroupDepths' )
|
|
vars+=( 'Overlays' )
|
|
vars+=( 'UsedOverlays' )
|
|
vars+=( 'OverlayExcludes' )
|
|
vars+=( 'OverlayInfo' 'Dir2OverlayMap')
|
|
vars+=( 'PmFiles' )
|
|
vars+=( 'ModulePathAppend' )
|
|
vars+=( 'ModulePathPrepend' )
|
|
vars+=( 'MaskedGroups' )
|
|
local s=''
|
|
s=$(typeset -p "${vars[@]}")
|
|
declare -gx PMODULES_ENV=$( encode_base64 "$s" )
|
|
}
|
|
|
|
#..............................................................................
|
|
declare -A vars_to_be_exported=()
|
|
export_env() {
|
|
local -r __doc__='Export required environment variables.'
|
|
local -A export_functions=()
|
|
export_functions['sh']='export_env_sh'
|
|
export_functions['bash']='export_env_sh'
|
|
export_functions['zsh']='export_env_sh'
|
|
export_functions['csh']='export_env_csh'
|
|
export_functions['tcsh']='export_env_csh'
|
|
export_functions['python']='export_env_python'
|
|
|
|
export_env_sh(){
|
|
while (( $# > 0 )); do
|
|
printf "export %s=\"%s\"; " "$1" "${!1}"
|
|
shift
|
|
done
|
|
}
|
|
export_env_csh(){
|
|
while (( $# > 0 )); do
|
|
printf "setenv %s \"%s\"; " "$1" "${!1}"
|
|
shift
|
|
done
|
|
}
|
|
export_env_python(){
|
|
while (( $# > 0 )); do
|
|
printf "os.environ['%s'] = '%s'\n" "$1" "${!1}"
|
|
shift
|
|
done
|
|
}
|
|
[[ -v vars_to_be_exported['PMODULES_ENV'] ]] && save_env
|
|
[[ -v export_functions[${Shell}] ]] || die_args_unsupported_shell "${Shell}"
|
|
${export_functions[${Shell}]} "${!vars_to_be_exported[@]}"
|
|
}
|
|
|
|
#..............................................................................
|
|
_exit() {
|
|
local -r __doc__='Function called on exit via trap.'
|
|
export_env
|
|
if [[ -n "${TmpFile}" ]] && [[ -e "${TmpFile}" ]]; then
|
|
${rm} -f "${TmpFile}" || :
|
|
fi
|
|
}
|
|
trap '_exit' EXIT
|
|
|
|
#..............................................................................
|
|
# Error messages
|
|
die_args_unsupported_shell(){
|
|
std::die 1 "${CMD}: unsupported shell -- $1"
|
|
}
|
|
|
|
die_args_subcmd_missing(){
|
|
std::die 1 "${CMD}: no sub-command specified."
|
|
}
|
|
|
|
die_args_invalid_subcmd(){
|
|
std::die 1 "${CMD}: unknown sub-command -- $1"
|
|
}
|
|
|
|
die_args_missing(){
|
|
std::die 3 "%s %s: %s\n" \
|
|
"${CMD}" "${SubCommand}" 'missing argument'
|
|
}
|
|
|
|
die_args_too_many(){
|
|
std::die 3 "%s %s: %s\n" \
|
|
"${CMD}" "${SubCommand}" 'too many arguments'
|
|
}
|
|
|
|
die_args_not_allowed(){
|
|
std::die 3 "%s %s: %s\n" \
|
|
"${CMD}" "${SubCommand}" "no arguments allowed"
|
|
}
|
|
|
|
die_args_wrong_number(){
|
|
"${CMD}" "${SubCommand}" "invalid option" "$1"
|
|
}
|
|
|
|
die_args_invalid_for_subcmd_use(){
|
|
std::die 1 "%s %s: %s -- %s\n" \
|
|
"${CMD}" "${SubCommand}" "invalid argument" "$1"
|
|
}
|
|
|
|
die_args_invalid_value(){
|
|
std::die 1 "%s %s: %s -- %s" \
|
|
"${CMD}" 'search' \
|
|
"invalid value for option '$1'" \
|
|
"$2"
|
|
}
|
|
|
|
die_grp_invalid(){
|
|
std::die 3 "%s %s: %s -- %s\n" \
|
|
"${CMD}" "${SubCommand}" "invalid group" "$1"
|
|
}
|
|
|
|
die_grp_cannot_be_removed(){
|
|
std::die 3 "%s %s: %s -- %s\n" \
|
|
"${CMD}" "${SubCommand}" "cannot remove group due to loaded modules" "$1"
|
|
}
|
|
|
|
die_relstage_invalid(){
|
|
std::die 3 "%s %s: %s -- %s\n" \
|
|
"${CMD}" "${SubCommand}" "invalid release stage" "$1"
|
|
}
|
|
|
|
die_module_unavail(){
|
|
std::die 3 "%s %s: %s -- %b\n" \
|
|
"${CMD}" "${SubCommand}" "not available in the current MODULEPATH" "$1"
|
|
}
|
|
|
|
die_module_nexist(){
|
|
std::die 3 "%s %s: module does not exist -- %s" \
|
|
"${CMD}" "${SubCommand}" "$1"
|
|
}
|
|
|
|
die_module_conflict(){
|
|
std::die 3 "%s %s: %s -- %b\n" \
|
|
"${CMD}" "${SubCommand}" \
|
|
"module conflicts with already loaded modules" "$1"
|
|
}
|
|
|
|
die_module_cmd_failed(){
|
|
std::die 3 "%s %s: %s" \
|
|
"${CMD}" "${SubCommand}" \
|
|
"failed"
|
|
}
|
|
|
|
die_module_not_a_modulefile(){
|
|
std::die 3 "%s %s: %s -- %s" \
|
|
"${CMD}" "${SubCommand}" \
|
|
"not a modulefile" "$1"
|
|
}
|
|
|
|
die_col_cannot_be_saved(){
|
|
std::die 3 "%s %s: %s -- %s" \
|
|
"${CMD}" "${SubCommand}" \
|
|
"cannot save_collection" "$1"
|
|
}
|
|
|
|
die_col_invalid_name(){
|
|
std::die 3 "%s %s: %s -- %s" \
|
|
"${CMD}" "${SubCommand}" \
|
|
"invalid collection name" "$1"
|
|
}
|
|
|
|
die_col_doesnt_exist(){
|
|
std::die 3 "%s %s: %s -- %s" \
|
|
"${CMD}" "${SubCommand}" \
|
|
"collection doesn't exist or isn't readable" "$1"
|
|
}
|
|
|
|
die_col_cannot_be_removed(){
|
|
std::die 3 "%s %s: %s -- %s" \
|
|
"${CMD}" "${SubCommand}" \
|
|
"cannot remove collection" "$1"
|
|
}
|
|
|
|
die_ol_cannot_be_added(){
|
|
std::die 3 "%s %s: %s -- %s" \
|
|
"${CMD}" "${SubCommand}" \
|
|
"overlay cannot be added since some modules are already loaded!" "$1"
|
|
}
|
|
|
|
die_ol_conflict(){
|
|
std::die 3 "%s %s: %s -- %s" \
|
|
"${CMD}" "${SubCommand}" \
|
|
"Overlay '$1' conflicts with" "$2"
|
|
}
|
|
|
|
die_ol_cannot_be_removed(){
|
|
std::die 3 "%s %s: %s" \
|
|
"${CMD}" "${SubCommand}" \
|
|
"overlay cannot be removed since some modules are still loaded!"
|
|
}
|
|
|
|
die_ol_cannot_remove_base(){
|
|
std::die 3 "%s %s: %s" \
|
|
"${CMD}" "${SubCommand}" \
|
|
"cannot remove base overlay!"
|
|
}
|
|
|
|
die_ol_not_used(){
|
|
std::die 3 "%s %s: %s -- %s" \
|
|
"${CMD}" "${SubCommand}" \
|
|
"not an used overlay" \
|
|
"$1"
|
|
}
|
|
|
|
die_ol_not_on_top_of_stack(){
|
|
std::die 3 "%s %s: %s %s -- %s" \
|
|
"${CMD}" "${SubCommand}" \
|
|
"overlay cannot be removed since" \
|
|
"it is not on top of the stack" \
|
|
"$1"
|
|
}
|
|
|
|
print_deprecated_msg(){
|
|
local -- msg=''
|
|
printf -v msg "
|
|
#############################################
|
|
# DEPRECATED MODULE - IMPORTANT INFORMATION #
|
|
#############################################
|
|
|
|
This module is deprecated and should no longer be used.
|
|
|
|
Please always use modules from the stable repository. Modules
|
|
from the unstable repository may also be used, but please
|
|
note that they are subject to change and may not be fully
|
|
tested.
|
|
|
|
Deprecated modules are no longer maintained, and support
|
|
requests related to them will not be addressed.
|
|
|
|
Thank you for your understanding.
|
|
"
|
|
std::info "%s" "${msg}"
|
|
}
|
|
|
|
print_module_load_msg(){
|
|
local -r relstage="$1"
|
|
local -- msg=''
|
|
[[ "${relstage}" == 'stable' ]] && return 0
|
|
printf -v msg "%s %s: %s -- %s" \
|
|
"${CMD}" 'load' \
|
|
"${relstage} module has been loaded" \
|
|
"${m}"
|
|
std::info "%s" "${msg}"
|
|
}
|
|
|
|
log_module_load_msg(){
|
|
local -r modulefile="$1"
|
|
local -r relstage="$2"
|
|
printf -v msg "%s: %s %s %s" \
|
|
'load' \
|
|
"modulefile=${modulefile}" \
|
|
"rel-stage=${relstage}" \
|
|
"user=${USER}"
|
|
${logger} -t Pmodules "${msg}"
|
|
}
|
|
|
|
#..............................................................................
|
|
get_module_config(){
|
|
local -r __doc__='
|
|
Read module configuration.
|
|
|
|
If a file ".config-<version>" exists, read configuration from this
|
|
file. The file must be in YAML format. The following keys are
|
|
supported:
|
|
|
|
RelStage: <relstage>
|
|
Systems: <list-of-systems>
|
|
Blocklist: <list-of-host-names>
|
|
|
|
If the above configuration file does not exist, get the release
|
|
stage from ".release-<version>". Restrictions to systems or
|
|
blocking hosts is not possible.
|
|
|
|
Note:
|
|
- the release stage of a module inside a Pmodules hierarchy
|
|
without a config file is always "unstable".
|
|
- the release stage of a module inside a Lmod hierarchy created
|
|
by Spack depends on the used release stages:
|
|
- if "unstable" is used, the release stage is "unstable"
|
|
- otherwise it is "stable".
|
|
- the release stage of other modules without a config file is
|
|
always "stable".
|
|
'
|
|
local -n ref_cfg="$1" # [out] reference to a dictionary to
|
|
# return the configuration
|
|
local -r dir="$2" # [in] directory containing modulefile
|
|
local -r modulefile="${dir}/$3" # [in] module name (inkl. version
|
|
# and/or sub-dirs)
|
|
|
|
ref_cfg['relstage']='unstable'
|
|
ref_cfg['systems']=''
|
|
ref_cfg['blocklist']=''
|
|
|
|
local -r config_file="${modulefile%/*}/.config-${modulefile##*/}"
|
|
local -r relstage_file="${modulefile%/*}/.release-${modulefile##*/}"
|
|
if [[ ! -r ${config_file} ]]; then
|
|
local -- ol_name=''
|
|
local -- group=''
|
|
find_overlay ol_name group "${modulefile}"
|
|
if [[ "${OverlayInfo[${ol_name}:layout]}" == 'Pmodules' ]]; then
|
|
[[ -r ${relstage_file} ]] && \
|
|
ref_cfg['relstage']=$( < "${relstage_file}" )
|
|
else
|
|
ref_cfg['relstage']='stable'
|
|
fi
|
|
return 0
|
|
fi
|
|
local -- yaml=''
|
|
yaml=$(${yq} -e '.' < "${config_file}")
|
|
local -- key=''
|
|
for key in "${!ref_cfg[@]}"; do
|
|
case "${key,,}" in
|
|
systems | blocklist )
|
|
value=$( ${yq} -e ".${key}[]" \
|
|
2>/dev/null <<<"${yaml}") ||
|
|
value='null'
|
|
;;
|
|
* )
|
|
value=$( ${yq} -e ".${key}" \
|
|
2>/dev/null <<<"${yaml}") ||
|
|
value='null'
|
|
;;
|
|
esac
|
|
[[ "${value}" != 'null' ]] && ref_cfg[${key,,}]="${value}"
|
|
done
|
|
return 0
|
|
}
|
|
|
|
#..............................................................................
|
|
is_available(){
|
|
local -r __doc__='
|
|
Module is available if
|
|
- release stage is used
|
|
- the systems list is empty or the system is in the list
|
|
- the blocklist is empty or the hostname is NOT in the list
|
|
'
|
|
local -n ref_cfg="$1"
|
|
local -- relstages="$2"
|
|
|
|
check_relstage(){
|
|
[[ ":${relstages}:" == *:${ref_cfg['relstage']}:* ]]
|
|
}
|
|
check_blocklist(){
|
|
[[ -z ${ref_cfg['blocklist']} ]] && return 0
|
|
local -- s=''
|
|
for s in ${ref_cfg['blocklist']}; do
|
|
if [[ "${OSRelease}" =~ $s ]] || [[ "${HostName}" =~ $s ]]; then
|
|
return 0
|
|
fi
|
|
done
|
|
return 1
|
|
}
|
|
check_systems(){
|
|
[[ -z ${ref_cfg['systems']} ]] && return 0
|
|
local -- s=''
|
|
for s in ${ref_cfg['systems']}; do
|
|
if [[ "${OSRelease}" =~ $s ]] || [[ "${HostName}" =~ $s ]]; then
|
|
return 0
|
|
fi
|
|
done
|
|
return 1
|
|
}
|
|
set -o noglob
|
|
check_relstage && check_blocklist && check_systems
|
|
local -i ec=$?
|
|
set +o noglob
|
|
return ${ec}
|
|
}
|
|
|
|
#..............................................................................
|
|
is_release_stage() {
|
|
local -r __doc__='
|
|
check whether the argument in $1 is a valid release stage.
|
|
'
|
|
[[ :${ReleaseStages}: =~ :$1: ]]
|
|
}
|
|
|
|
#..............................................................................
|
|
find_overlay () {
|
|
local -r __doc__='
|
|
Check whether a given moduledir is in an used overlay.
|
|
If yes, return 0 otherwise return 1
|
|
'
|
|
local -n ref_ol="$1" # [out] ref.var to return overlay name
|
|
local -n ref_group="$2" # [out] ref.var to return group
|
|
local -- path="$3" # [in] moduledir to check
|
|
|
|
path="${path%/"${__MODULEFILES_DIR__}"*}"
|
|
local -- ol=''
|
|
for ol in "${UsedOverlays[@]}"; do
|
|
local modulefiles_root="${OverlayInfo[${ol}:modulefiles_root]}"
|
|
if [[ "${path}" == ${modulefiles_root}/* ]]; then
|
|
ref_ol="${ol}"
|
|
if [[ "${OverlayInfo[${ref_ol}:layout]}" == 'Pmodules' ]]; then
|
|
ref_group="${path#"${OverlayInfo[${ol}:modulefiles_root]}"/}"
|
|
else
|
|
ref_group='none'
|
|
fi
|
|
return 0
|
|
fi
|
|
done
|
|
ref_ol='none'
|
|
ref_group='none'
|
|
return 1
|
|
}
|
|
|
|
#..............................................................................
|
|
is_modulefile() {
|
|
local -r __doc__='
|
|
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
|
|
|
|
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 does not exist
|
|
'
|
|
local -n ref_intrp="$1" # [out] ref. variable for result
|
|
local -r fname="$2" # [in] absolute file name to test
|
|
|
|
# is this a regular, readable file?
|
|
[[ -f "${fname}" && -r "${fname}" ]] || return 2
|
|
|
|
if [[ "${fname##*.}" == 'lua' ]]; then
|
|
ref_intrp="${Lmod_cmd}"
|
|
return 0
|
|
fi
|
|
local -- shebang
|
|
read -r -n 11 shebang < "${fname}"
|
|
if [[ "${shebang:0:8}" == '#%Module' ]] \
|
|
|| [[ "${shebang:0:9}" == '#%Pmodule' ]]; then
|
|
ref_intrp="${Tcl_cmd}"
|
|
return 0
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
#..............................................................................
|
|
get_module_prefix() {
|
|
local -r __doc__='
|
|
Get the value of <module>_PREFIX.
|
|
'
|
|
local -n ref_prefix="$1" # [out] ref. variable to return result
|
|
local -- modulefile="$2" # [in] modulefile
|
|
ref_prefix=$("${modulecmd}" bash show "${modulefile}" 2>&1 | \
|
|
${awk} '/_PREFIX |_HOME / {print $3; exit}')
|
|
}
|
|
|
|
###############################################################################
|
|
# Generic wrappers for the modulecmd (Tcl and Lmod):
|
|
#
|
|
# The options to output help are supported.
|
|
#
|
|
#..............................................................................
|
|
subcommand_generic0() {
|
|
local -r __doc__='
|
|
Call modulecmd without arguments.
|
|
'
|
|
local -a args=()
|
|
while (( $# > 0 )); do
|
|
case $1 in
|
|
-\? | -H | --help )
|
|
print_help "${SubCommand}"
|
|
;;
|
|
-- )
|
|
shift 1
|
|
args+=( "$@" )
|
|
break
|
|
;;
|
|
* )
|
|
args+=( "$1" )
|
|
;;
|
|
esac
|
|
shift 1
|
|
done
|
|
(( ${#args[@]} == 0 )) || \
|
|
die_args_not_allowed
|
|
"${modulecmd}" "${Shell}" "${SubCommand}"
|
|
}
|
|
#..............................................................................
|
|
subcommand_generic1() {
|
|
local -r __doc__='
|
|
Call modulecmd with exactly one argument.
|
|
'
|
|
local -a args=()
|
|
while (( $# > 0 )); do
|
|
case $1 in
|
|
-\? | -H | --help )
|
|
print_help "${SubCommand}"
|
|
;;
|
|
-- )
|
|
shift 1
|
|
args+=( "$@" )
|
|
break
|
|
;;
|
|
* )
|
|
args+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
(( ${#args[@]} == 0 )) && \
|
|
die_args_missing
|
|
(( ${#args[@]} > 1 )) && \
|
|
die_args_too_many
|
|
"${modulecmd}" "${Shell}" "${SubCommand}" "${args[@]}"
|
|
}
|
|
#..............................................................................
|
|
subcommand_generic1plus() {
|
|
local -r __doc__='
|
|
Call modulecmd with one or more arguments.
|
|
'
|
|
local args=()
|
|
while (( $# > 0 )); do
|
|
case $1 in
|
|
-\? | -H | --help )
|
|
print_help "${SubCommand}"
|
|
;;
|
|
-- )
|
|
shift 1
|
|
args+=( "$@" )
|
|
break
|
|
;;
|
|
* )
|
|
args+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
(( ${#args[@]} == 0 )) && \
|
|
die_args_missing
|
|
"${modulecmd}" "${Shell}" "${SubCommand}" "${args[@]}"
|
|
}
|
|
#..............................................................................
|
|
set_lmfiles(){
|
|
local -r __doc__='
|
|
Set/fix the environment variable _LMFILES_ and LOADEDMODULES.
|
|
|
|
Why do we have to do this?
|
|
- Lmod removes files from _LMFILE_ which have been loaded
|
|
by the Tcl modulecmd. Even worse Lmod might unset _LMFILE_.
|
|
If the modulecmd was Lmod, the Tcl module files have to
|
|
be added again to _LMFILE_.
|
|
- After fixing _LMFILE_ we rebuild LOADEDMODULES from
|
|
_LMFILE_.
|
|
'
|
|
if [[ ! -v _LMFILES_ ]]; then
|
|
declare -gx _LMFILES_=''
|
|
declare -gx LOADEDMODULES=''
|
|
fi
|
|
local -- dir=''
|
|
local -a dirs=()
|
|
if [[ "${modulecmd}" == "${Lmod_cmd}" ]]; then
|
|
IFS=':' read -r -a dirs <<<"${PmFiles}"
|
|
for dir in "${dirs[@]}"; do
|
|
std::append_path _LMFILES_ "${dir}"
|
|
done
|
|
fi
|
|
if [[ -z "${_LMFILES_}" ]]; then
|
|
return 0
|
|
fi
|
|
# rebuild LOADEDMODULES by setting it to _LMFILES_ and then removing
|
|
# all directories given in MODULEPATH from LOADEDMODULES.
|
|
LOADEDMODULES="${_LMFILES_}"
|
|
IFS=':' read -r -a dirs <<<"${MODULEPATH}"
|
|
for dir in "${dirs[@]}"; do
|
|
# If the first or last character of MODULEPATH is ':',
|
|
# we get an empty string. This shouldn't happen, but
|
|
# with this test we are on the save side.
|
|
[[ -z ${dir} ]] && continue
|
|
|
|
# Skip relative directories in MODULEPATH.
|
|
# Relative directories in MODULEPATH doesn't make make much
|
|
# sense. Or? For now we just ignore them.
|
|
[[ "${dir:0:1}" == '/' ]] || continue
|
|
|
|
# The directory string we want to remove must end with a
|
|
# slash, otherwise we have entries in LOADEDMODULES
|
|
# beginning with a slash.
|
|
[[ "${dir: -1}" == "/" ]] || dir+="/"
|
|
|
|
# Remove this directory from all entries in LOADEDMODULES
|
|
LOADEDMODULES="${LOADEDMODULES//${dir}}"
|
|
done
|
|
}
|
|
|
|
##############################################################################
|
|
Subcommands['add']='load'
|
|
Subcommands['load']='load'
|
|
Options['load']='-l help -o \?Hfsvw -l force -l silent -l verbose -l warn'
|
|
Help['load']='
|
|
USAGE:
|
|
module add modulefile...
|
|
module load modulefile...
|
|
Load modulefile(s) into the shell environment. Loading a
|
|
"group-head" will extend the MODULEPATH. E.g.: loading a
|
|
compiler makes additional modules like openmpi and libraries
|
|
compiled with this compiler available.
|
|
'
|
|
#..............................................................................
|
|
subcommand_load() {
|
|
local -r __doc__='
|
|
Load a module. The following formats are supported for the
|
|
name of the module to be loaded:
|
|
|
|
- name
|
|
- name/version
|
|
- group:name
|
|
- group:name/version
|
|
- group:name:relstage
|
|
- group:name/version:relstage
|
|
- name:relstage
|
|
'
|
|
local -- relstage='undef'
|
|
local -- current_modulefile=''
|
|
local -- prefix=''
|
|
local -- m=''
|
|
local -A interp
|
|
|
|
#......................................................................
|
|
# output load 'hints'
|
|
#
|
|
# Note:
|
|
# The variable 'm' from the parent function will be used
|
|
# but not changed.
|
|
#
|
|
get_load_hints() {
|
|
local -n output="$1" # [out] ref.var to return result
|
|
local relstage=''
|
|
output=''
|
|
while read -r -a line; do
|
|
(( ${#line[@]} == 0 )) && continue
|
|
relstage=${line[1]}
|
|
if [[ ! ":${UsedReleaseStages}:" == *:${relstage}:* ]]; then
|
|
output+="module use ${relstage}; "
|
|
fi
|
|
local group=${line[2]}
|
|
[[ "${group}" != 'none' ]] || continue
|
|
if [[ ! ":${UsedGroups}:" == *:${group}:* ]] && \
|
|
(( ${GroupDepths[${group}]} == 0 )); then
|
|
output+="module use ${group}; "
|
|
fi
|
|
local -i n=$(( ${GroupDepths[${group}]}/2 ))
|
|
output+="module load ${line[*]:4:$n} ${line[0]}\n"
|
|
done < <(set +x; subcommand_search "${m}" -a --no-header 2>&1)
|
|
if [[ -n ${output} ]]; then
|
|
output="\n\nTry with one of the following command(s):\n${output}"
|
|
fi
|
|
}
|
|
|
|
#......................................................................
|
|
module_is_loaded() {
|
|
[[ :${LOADEDMODULES}: =~ :$1: ]]
|
|
}
|
|
|
|
#......................................................................
|
|
load_dependencies() {
|
|
local -r fname="$1"
|
|
local -- dep=''
|
|
while read -r dep; do
|
|
[[ -z ${dep} ]] && continue
|
|
[[ ${dep:0:1} == \# ]] && continue
|
|
module_is_loaded "${dep}" && continue
|
|
subcommand_load "${dep}"
|
|
done < "${fname}"
|
|
}
|
|
|
|
#......................................................................
|
|
local args=()
|
|
local opts=()
|
|
local overlay
|
|
while (($# > 0)); do
|
|
case $1 in
|
|
-\? | -H | --help )
|
|
print_help "${SubCommand}"
|
|
;;
|
|
-f | --force )
|
|
opts+=(' -f')
|
|
;;
|
|
-s | --silent )
|
|
Verbosity_lvl='silent'
|
|
;;
|
|
-v | --verbose )
|
|
Verbosity_lvl='verbose'
|
|
;;
|
|
-w | --warn )
|
|
Verbosity_lvl='warn'
|
|
;;
|
|
-- )
|
|
shift 1
|
|
args+=( "$@" )
|
|
break
|
|
;;
|
|
* )
|
|
args+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
(( ${#args[@]} == 0 )) && \
|
|
die_args_missing
|
|
if [[ ! -v LOADEDMODULES ]]; then
|
|
LOADEDMODULES=''
|
|
_LMFILES_=''
|
|
fi
|
|
|
|
local m=''
|
|
for m in "${args[@]}"; do
|
|
if [[ "$m" == *:* ]]; then
|
|
local -a toks=()
|
|
IFS=':' read -r -a toks <<< "${m}"
|
|
local -- group=''
|
|
local -- relstage=''
|
|
if (( ${#toks[@]} > 3 )); then
|
|
: # invalid name
|
|
elif (( ${#toks[@]} == 3 )); then
|
|
group="${toks[0]}"
|
|
[[ -v GroupDepths[${group}] ]] || \
|
|
die_grp_invalid
|
|
m="${toks[1]}"
|
|
relstage="${toks[2]}"
|
|
else
|
|
if [[ -v GroupDepths[${toks[0]}] ]]; then
|
|
group="${toks[0]}"
|
|
m="${toks[1]}"
|
|
else
|
|
m="${toks[0]}"
|
|
relstage="${toks[1]}"
|
|
fi
|
|
fi
|
|
if [[ -n ${group} ]]; then
|
|
local -i depth=${GroupDepths[${group}]}
|
|
(( depth != 0 )) && \
|
|
die_grp_invalid "${group}"
|
|
subcommand_use "${group}"
|
|
fi
|
|
if [[ -n ${relstage} ]]; then
|
|
is_release_stage "${relstage}" || \
|
|
die_relstage_invalid "${relstage}"
|
|
subcommand_use "${relstage}"
|
|
fi
|
|
fi # handle extended module names
|
|
|
|
find_modulefile current_modulefile relstage moduledir modulecmd "${m}" || {
|
|
local hints=''
|
|
get_load_hints hints
|
|
if [[ -z "${hints}" ]]; then
|
|
die_module_nexist "${m}"
|
|
else
|
|
die_module_unavail "${m}${hints}"
|
|
fi
|
|
}
|
|
|
|
# If the user wants to load/switch to another Pmodules version:
|
|
# This is possible if
|
|
# - no other module is loaded
|
|
# - the loaded version of Pmodules is >= 1.1.22
|
|
# - the to be loaded version of Pmodules is >= 1.1.22
|
|
if [[ ${m} == Pmodules/* ]]; then
|
|
local -r new_version="${m##*/}"
|
|
[[ -v Version ]] || Version='0.0.0'
|
|
if [[ -n ${LOADEDMODULES} ]]; then
|
|
if std::version_lt "${new_version}" '1.1.22' || \
|
|
std::version_lt "${Version}" '1.1.22'; then
|
|
die_module_conflict "${m}"
|
|
fi
|
|
Version="${new_version}"
|
|
fi
|
|
fi
|
|
|
|
# nothing to do if already loaded, continue with
|
|
# next module to be loaded
|
|
[[ ":${LOADEDMODULES}:" == *:${m}:* ]] && continue
|
|
|
|
# show info file
|
|
local prefix=''
|
|
get_module_prefix prefix "${current_modulefile}"
|
|
[[ -n ${prefix} && -r "${prefix}/.info" ]] && cat "${prefix}/.info" 1>&2
|
|
|
|
# loading the dependencies overwrites the interpreter for the
|
|
# currend modulefile, so we have to save it for later use.
|
|
interp[${current_modulefile}]="${modulecmd}"
|
|
|
|
# load dependencies
|
|
local -- deps_file="${current_modulefile%/*}/.deps-${current_modulefile##*/}"
|
|
if [[ ! -r "${deps_file}" && -n "${prefix}" ]]; then
|
|
deps_file="${prefix}/.dependencies"
|
|
fi
|
|
test -r "${deps_file}" && load_dependencies "$_"
|
|
|
|
# load module
|
|
modulecmd="${interp[${current_modulefile}]}"
|
|
local output=''
|
|
output=$("${modulecmd}" 'bash' "${opts[@]}" 'load' \
|
|
"${current_modulefile}" 2> "${TmpFile}")
|
|
|
|
# we don't print the error message we got from modulecmd, due to
|
|
# readability.
|
|
# :FIXME:
|
|
# Not sure whether this is now correct!
|
|
# The idea is to supress the error messages from the Tcl
|
|
# modulecmd, but not the output to stderr coded in a
|
|
# modulefile.
|
|
# :FIXME:
|
|
# Handle errors from Lmod.
|
|
# :FIXME:
|
|
# In some cases the error message is unclear.
|
|
local error=''
|
|
error=$( < "${TmpFile}")
|
|
if [[ "${error}" == *:ERROR:* ]]; then
|
|
local s=${error%%$'\n'*}
|
|
[[ "$s" =~ ' conflicts ' ]] && \
|
|
die_module_conflict "${m}"
|
|
die_module_cmd_failed "${m}"
|
|
fi
|
|
if [[ "${Shell}" == "sh" ]]; then
|
|
# for sh-like shells just echo
|
|
echo "${output}"
|
|
else
|
|
# re-run with right shell
|
|
"${modulecmd}" "${Shell}" "${opts[@]}" 'load' \
|
|
"${current_modulefile}"
|
|
fi
|
|
output=$( ${sed} -e 's/source [^;]*;//g' <<<"${output}" )
|
|
eval "${output}"
|
|
if [[ -n "${error}" ]]; then
|
|
echo "${error}" 1>&2
|
|
fi
|
|
if [[ "${modulecmd}" == "${Tcl_cmd}" ]]; then
|
|
std::append_path PmFiles "${current_modulefile}"
|
|
fi
|
|
|
|
if [[ ${Verbosity_lvl} != silent ]]; then
|
|
[[ "${relstage}" == 'deprecated' ]] && print_deprecated_msg
|
|
print_module_load_msg "${relstage}"
|
|
fi
|
|
log_module_load_msg "${current_modulefile}" "${relstage}"
|
|
done
|
|
set_lmfiles
|
|
vars_to_be_exported['PMODULES_ENV']=1
|
|
vars_to_be_exported['LOADEDMODULES']=1
|
|
vars_to_be_exported[]'_LMFILES_']=1
|
|
}
|
|
|
|
##############################################################################
|
|
Subcommands['rm']='unload'
|
|
Subcommands['unload']='unload'
|
|
Options['unload']='-o \?H -l help'
|
|
Help['unload']='
|
|
USAGE:
|
|
module rm modulefile...
|
|
module unload modulefile...
|
|
Remove modulefile(s) from the shell environment. Removing
|
|
a "group-head" will also unload all modules belonging to
|
|
this group.
|
|
'
|
|
#..............................................................................
|
|
subcommand_unload() {
|
|
local -r __doc__='
|
|
Unload a module.
|
|
|
|
:FIXME:
|
|
add dependency tests: do not unload if module is required
|
|
be another module. For the time being the modules requiring
|
|
this module will be unloaded too.
|
|
'
|
|
local args=()
|
|
while (( $# > 0 )); do
|
|
case $1 in
|
|
-\? | -H | --help )
|
|
print_help "${SubCommand}"
|
|
;;
|
|
-- )
|
|
shift 1
|
|
args+=( "$@" )
|
|
break
|
|
;;
|
|
-* )
|
|
die_args_invalid_opt "$1"
|
|
;;
|
|
|
|
* )
|
|
args+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
(( ${#args[@]} == 0 )) && \
|
|
die_args_missing
|
|
|
|
# The module() function uses PMODULES_HOME to call modulecmd.
|
|
# If a Pmodules module is unloaded this evnvironment variable
|
|
# will be unset. In consequence the module() function would
|
|
# fail. Instead of comparing the name of the module to 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}"
|
|
|
|
if [[ ! -v _LMFILES_ ]]; then
|
|
declare -x _LMFILES_=''
|
|
declare -x LOADEDMODULES=''
|
|
fi
|
|
|
|
IFS=':' read -r -a _lmfiles_ <<< "${_LMFILES_}"
|
|
local arg
|
|
local lmfile
|
|
for arg in "${args[@]}"; do
|
|
# is the module loaded?
|
|
for lmfile in "${_lmfiles_[@]}" '_zzzz_'; do
|
|
if [[ $lmfile =~ ${arg} ]]; then
|
|
break
|
|
fi
|
|
done
|
|
if [[ "${lmfile}" == '_zzzz_' ]]; then
|
|
continue
|
|
fi
|
|
# yes, module has been loaded
|
|
is_modulefile modulecmd "${lmfile}" || die_module_not_a_modulefile "${arg}"
|
|
|
|
local output=''
|
|
output=$("${modulecmd}" 'bash' 'unload' "${arg}")
|
|
if [[ -n "${output}" ]]; then
|
|
eval "$(echo "${output}"|${sed} -e 's/;unalias [^;]*//g')"
|
|
fi
|
|
case ${Shell} in
|
|
sh | bash | zsh )
|
|
echo "${output}"
|
|
echo ""
|
|
;;
|
|
* )
|
|
"${modulecmd}" "${Shell}" 'unload' "${arg}"
|
|
echo ""
|
|
;;
|
|
esac
|
|
if [[ "${modulecmd}" == "${Tcl_cmd}" ]]; then
|
|
std::remove_path PmFiles "${lmfile}"
|
|
fi
|
|
done
|
|
if [[ ! -v PMODULES_HOME || -z ${PMODULES_HOME} ]]; then
|
|
declare -gx PMODULES_HOME="${saved_home}"
|
|
vars_to_be_exported['PMODULES_HOME']=1
|
|
fi
|
|
set_lmfiles
|
|
|
|
# maybe Lmod removed some directories from MODULEPATH?
|
|
local -- dir=''
|
|
local -a dirs=()
|
|
IFS=':' read -r -a dirs <<<"${ModulePathAppend}"
|
|
for dir in "${dirs[@]}"; do
|
|
std::append_path MODULEPATH "${dir}"
|
|
done
|
|
IFS=':' read -r -a dirs <<<"${ModulePathPrepend}"
|
|
for dir in "${dirs[@]}"; do
|
|
std::prepend_path MODULEPATH "${dir}"
|
|
done
|
|
if [[ -n "${LOADEDMODULES}" ]]; then
|
|
vars_to_be_exported['LOADEDMODULES']=1
|
|
vars_to_be_exported['_LMFILES_']=1
|
|
fi
|
|
vars_to_be_exported['PMODULES_ENV']=1
|
|
} # subcommand_unload
|
|
|
|
##############################################################################
|
|
Subcommands['switch']='swap'
|
|
Subcommands['swap']='swap'
|
|
Options['swap']='-o \?H -l help'
|
|
Help['swap']='
|
|
USAGE:
|
|
module switch [modulefile1] modulefile2
|
|
module swap [modulefile1] modulefile2
|
|
Switch loaded modulefile1 with modulefile2. If modulefile1
|
|
is not specified, then it is assumed to be the currently
|
|
loaded module with the same base name as modulefile2.
|
|
'
|
|
#..............................................................................
|
|
subcommand_swap() {
|
|
local -r __doc__='
|
|
Swap two modules.
|
|
'
|
|
local args=()
|
|
while (( $# > 0 )); do
|
|
case $1 in
|
|
-\? | -H | --help )
|
|
print_help "${SubCommand}"
|
|
;;
|
|
-- )
|
|
shift 1
|
|
args+=( "$@" )
|
|
break
|
|
;;
|
|
* )
|
|
args+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
(( ${#args[@]} == 0 )) && \
|
|
die_args_missing
|
|
(( ${#args[@]} > 2 )) && \
|
|
die_args_too_many
|
|
|
|
if (( ${#args[@]} == 1 )); then
|
|
local -r module_to_load=${args[0]}
|
|
local -r module_to_unload=${module_to_load%/*}
|
|
else
|
|
local -r module_to_unload=${args[0]}
|
|
local -r module_to_load=${args[1]}
|
|
fi
|
|
subcommand_unload "${module_to_unload}"
|
|
subcommand_load "${module_to_load}"
|
|
}
|
|
|
|
##############################################################################
|
|
Subcommands['display']='show'
|
|
Subcommands['show']='show'
|
|
Options['show']='-o \?H -l help'
|
|
Help['show']='
|
|
USAGE:
|
|
module display modulefile...
|
|
module show modulefile...
|
|
Display information about one or more modulefiles. The
|
|
display sub-command will list the full path of the
|
|
modulefile(s) and all (or most) of the environment changes
|
|
the modulefile(s) will make if loaded. It will not display
|
|
any environment changes found within conditional statements.
|
|
'
|
|
#..............................................................................
|
|
subcommand_show() {
|
|
local -r __doc__='Show module info.'
|
|
local -a args=()
|
|
while (( $# > 0 )); do
|
|
case $1 in
|
|
-\? | -H | --help )
|
|
print_help "${SubCommand}"
|
|
;;
|
|
-- )
|
|
shift 1
|
|
args+=( "$@" )
|
|
break
|
|
;;
|
|
* )
|
|
args+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
(( ${#args[@]} == 0 )) && \
|
|
die_args_missing
|
|
|
|
local arg
|
|
for arg in "${args[@]}"; do
|
|
local -- modulefile=''
|
|
local -- relstage=''
|
|
local -- moduledir=''
|
|
find_modulefile \
|
|
modulefile \
|
|
relstage \
|
|
moduledir \
|
|
modulecmd \
|
|
"${arg}"
|
|
"${modulecmd}" 'bash' 'show' "${modulefile}"
|
|
done
|
|
}
|
|
|
|
#..............................................................................
|
|
get_available_modules() {
|
|
local -r __doc__='
|
|
Find all modules in a given modulepath matching a specific
|
|
string. The search can be restricted to certain release stages.
|
|
|
|
Return list like
|
|
(
|
|
module_name_1
|
|
release_stage_1
|
|
directory_1
|
|
relative_modulefile_1
|
|
overlay_1
|
|
group_1
|
|
module_name_2
|
|
...
|
|
)'
|
|
local -- mode="$1"
|
|
local -n result="$2" # [out] reference variable to return result
|
|
local -r pattern="$3" # [in] search pattern
|
|
local -r relstages="$4" # [in] excepted release stages
|
|
shift 4
|
|
local -a dirs=("$@") # [in] module path (absolute directory names)
|
|
|
|
local -A modules=()
|
|
local -A modulenames=()
|
|
|
|
local -- fpattern="${pattern}" # pattern used in find(1)
|
|
if [[ ${pattern} == */* ]]; then
|
|
# if pattern contains a slash, match against
|
|
# the part before the last slash.
|
|
# Example:
|
|
# If pattern is gcc/14, we have to match the string 'gcc'
|
|
# followed by a slash and a string not containing a slash.
|
|
fpattern=".*/${pattern%/*}/[^/]+"
|
|
else
|
|
# otherwise match max one slash
|
|
fpattern=".*/${pattern}[^/]*/*[^/]+"
|
|
fi
|
|
local -- opt_regex='-regex'
|
|
[[ "${mode}" != 'load' ]] && opt_regex='-iregex'
|
|
local -- opt_sort='-r'
|
|
[[ -z ${pattern} ]] && opt_sort=''
|
|
|
|
local -- dir=''
|
|
# loop over all entries in given module path
|
|
for dir in "${dirs[@]}"; do
|
|
test -d "${dir}" || continue
|
|
# find overlay and group for this directory
|
|
local -- ol_name=''
|
|
local -- group=''
|
|
find_overlay ol_name group "${dir}"
|
|
|
|
# loop over all files (and sym-links) in this directory and
|
|
# its sub-directories
|
|
local -- short_module_name='' # module name
|
|
local -- long_module_name='' # module name & version
|
|
while read -r rel_modulefile; do
|
|
IFS='/' read -a toks <<<"${rel_modulefile}"
|
|
if (( ${#toks[@]} == 0 )); then
|
|
continue
|
|
elif (( ${#toks[@]} == 1 )); then
|
|
short_module_name="${mod}"
|
|
long_module_name="${toks[0]}"
|
|
else
|
|
short_module_name="${toks[-2]}"
|
|
long_module_name="${toks[-2]}/${toks[-1]}"
|
|
fi
|
|
[[ -n ${OverlayExcludes} \
|
|
&& "${mod}" =~ ${OverlayExcludes} ]] && continue
|
|
local add='no'
|
|
if [[ -n "${ol_name}" && "${ol_name,,}" != 'none' ]]; then
|
|
# module is in an overlay
|
|
#
|
|
# add to list of available modules, if
|
|
# - first time found by name only
|
|
# - in same overlay as first found
|
|
# - new version and not hidden by overlay
|
|
if [[ ! -v modulenames["${short_module_name}"] ]]; then
|
|
# new entry
|
|
if [[ "${OverlayInfo[${ol_name}:type]}" == "${ol_hiding}" ]]; then
|
|
modulenames[${short_module_name}]="${ol_name}"
|
|
else
|
|
modulenames[${short_module_name}]='0'
|
|
fi
|
|
add='yes'
|
|
elif [[ "${modulenames[${short_module_name}]}" == "${ol_name}" ]]; then
|
|
add='yes'
|
|
elif [[ "${modulenames[${short_module_name}]}" == '0' ]] \
|
|
&& [[ ! -v modules[${rel_modulefile}] ]]; then
|
|
add='yes'
|
|
fi
|
|
else
|
|
# :FIXME:
|
|
add='yes' # module is NOT in an overlay
|
|
fi
|
|
[[ "${add}" == 'no' ]] && continue
|
|
if [[ "${mode}" == 'search' ]]; then
|
|
[[ "${long_module_name,,}" =~ ${pattern,,} ]] || continue
|
|
else
|
|
if [[ "${pattern}" == */* ]]; then
|
|
[[ "${long_module_name}" == ${pattern} || \
|
|
"${long_module_name}" == ${pattern}.lua ]] || \
|
|
continue
|
|
else
|
|
[[ "${long_module_name%/*}" == ${pattern} ]] || continue
|
|
fi
|
|
fi
|
|
local -A cfg=()
|
|
local -- relstage='stable'
|
|
if [[ "${OverlayInfo[${ol_name}:layout]}" == 'Pmodules' ]]; then
|
|
get_module_config cfg "${dir}" "${rel_modulefile}"
|
|
is_available cfg "${relstages}" || continue
|
|
relstage="${cfg['relstage']}"
|
|
elif [[ "${OverlayInfo[${ol_name}:layout]}" == 'Spack' ]]; then
|
|
if [[ ":${UsedReleaseStages}:" =~ :unstable: ]]; then
|
|
relstage='unstable'
|
|
fi
|
|
else
|
|
get_module_config cfg "${dir}" "${rel_modulefile}"
|
|
is_available cfg "${ReleaseStages}" || continue
|
|
fi
|
|
modules[${rel_modulefile}]=1
|
|
|
|
result+=(
|
|
"${long_module_name}"
|
|
"${relstage}"
|
|
"${dir}"
|
|
"${rel_modulefile}"
|
|
"${ol_name}"
|
|
"${group}" )
|
|
|
|
# return after first match, e.g. for loading a module
|
|
[[ "${mode}" != 'search' ]] && return 0
|
|
done < <(${find} -L "${dir}" \
|
|
-not -name ".*" \
|
|
\( "${opt_regex}" "${fpattern}" \
|
|
-regextype posix-basic \) \
|
|
\( -type f -o -type l \) \
|
|
-printf "%P\n" \
|
|
| ${sort} ${opt_sort} --version-sort)
|
|
done
|
|
} # get_available_modules()
|
|
|
|
#..............................................................................
|
|
find_modulefile(){
|
|
local -r __doc__='
|
|
find module(file) to load. The module name can be
|
|
name
|
|
name/version
|
|
relative path
|
|
absolute path
|
|
|
|
Return
|
|
0 if a modulefile has been found
|
|
1 else'
|
|
local -n ref_modulefile="$1"
|
|
local -n ref_relstage="$2"
|
|
local -n ref_moduledir="$3"
|
|
local -n ref_interp="$4"
|
|
local -r modulename="$5"
|
|
|
|
local -a modulepath=()
|
|
IFS=':' read -r -a modulepath <<<"${MODULEPATH}"
|
|
local -a mods=()
|
|
local -- relstages="${UsedReleaseStages}"
|
|
[[ "${modulename}" = */* ]] && relstages="${ReleaseStages}"
|
|
get_available_modules \
|
|
'load' \
|
|
mods \
|
|
"${modulename}" \
|
|
"${relstages}" \
|
|
"${modulepath[@]}"
|
|
(( ${#mods[@]} == 0 )) && return 1
|
|
ref_relstage="${mods[1]}"
|
|
ref_moduledir="${mods[2]}"
|
|
ref_modulefile="${mods[2]}/${mods[3]}"
|
|
is_modulefile ref_interp "${ref_modulefile}" || \
|
|
die_module_not_a_modulefile "${modulename}"
|
|
if [[ "${modulecmd}" == "${Lmod_cmd}" ]]; then
|
|
# Lmod doesn't support full qualified path names!
|
|
ref_modulefile="${ref_modulefile/${ref_moduledir}\/}"
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
##############################################################################
|
|
Subcommands['avail']='avail'
|
|
Options['avail']='-l help -o \?Hahlmtg: -l all -l all-release-stages -l group: '
|
|
Options['avail']+='-l human -l long -l machine -l terse'
|
|
Help['avail']="
|
|
USAGE:
|
|
module avail [switches] string
|
|
List all available modulefiles in the current MODULEPATH. If
|
|
an argument is given, then each directory in the MODULEPATH
|
|
is searched for modulefiles whose pathname match the argument.
|
|
|
|
This command does *not* display all installed modules on the
|
|
system. Only *loadable* modules are listed. The list of
|
|
available modules may change either by loading other modules,
|
|
e.g. a compiler, or with the sub-command 'use'.
|
|
|
|
SWITCHES:
|
|
-a|--all||--all-release-stages
|
|
List all available modules independend of the release stage.
|
|
|
|
-t|--terse
|
|
Output in short format.
|
|
|
|
-l|--long
|
|
Output in long format.
|
|
|
|
-g|--group=<GROUP>
|
|
Output modules available in <GROUP>
|
|
|
|
-h|--human
|
|
Output in human readable format.
|
|
|
|
-m|--machine
|
|
Output in machine readable format
|
|
"
|
|
#..............................................................................
|
|
subcommand_avail() {
|
|
local -r __doc__='List available modules.'
|
|
# use this variable in the output functions
|
|
local -a mods=()
|
|
local -- dir=''
|
|
|
|
# get number of columns of terminal, set to a default if not running
|
|
# in a terminal.
|
|
local -i cols=80
|
|
[[ -t 1 && -t 2 ]] && cols=$(tput cols)
|
|
|
|
#......................................................................
|
|
|
|
output_header() {
|
|
local -i i=$1
|
|
|
|
# use group name, overlay name or directory
|
|
local -- caption="${mods[i+5]}" # group name
|
|
if [[ "${caption,,}" == 'none' ]]; then
|
|
caption="${mods[i+4]}" # overlay name
|
|
if [[ "${caption,,}" == 'none' ]]; then
|
|
caption="${mods[i+2]}" # directory
|
|
fi
|
|
fi
|
|
(( i != 0 )) && printf -- "\n\n" 1>&2
|
|
local -i i=0
|
|
(( i=(cols-${#caption})/2-2 ))
|
|
printf -- "%0.s-" $(seq 1 "$i") 1>&2
|
|
printf -- " %s " "${caption}" 1>&2
|
|
printf -- "%0.s-" $(seq 1 "$i") 1>&2
|
|
printf -- "\n" 1>&2
|
|
}
|
|
|
|
#......................................................................
|
|
terse_output() {
|
|
local -- cur_group=''
|
|
local -- cur_dir=''
|
|
|
|
local -i i=0
|
|
for (( i=0; i<${#mods[@]}; i+=6 )); do
|
|
if [[ "${cur_group}" != "${mods[i+5]}" ]] || \
|
|
[[ "${cur_group}" == 'none' && \
|
|
"${cur_dir}" != "${mods[i+2]}" ]]; then
|
|
output_header "$i"
|
|
cur_group="${mods[i+5]}"
|
|
cur_dir="${mods[i+2]}"
|
|
fi
|
|
|
|
local mod=${mods[i]%.lua}
|
|
local relstage=${mods[i+1]}
|
|
case ${relstage} in
|
|
stable )
|
|
out=''
|
|
;;
|
|
* )
|
|
out="${relstage}"
|
|
;;
|
|
esac
|
|
printf "%-20s\t%s\n" "${mod}" "${out}" 1>&2
|
|
done
|
|
std::info ""
|
|
}
|
|
|
|
#......................................................................
|
|
machine_output() {
|
|
for (( i=0; i<${#mods[@]}; i+=6 )); do
|
|
printf "%-20s\t%s\n" "${mods[i]}" "${mods[i+1]}" 1>&2
|
|
done
|
|
}
|
|
|
|
#......................................................................
|
|
# :FIXME: for the time being, this is the same as terse_output!
|
|
long_output() {
|
|
local -- cur_group=''
|
|
local -- cur_dir=''
|
|
|
|
local -i i=0
|
|
for (( i=0; i<${#mods[@]}; i+=6 )); do
|
|
if [[ "${cur_group}" != "${mods[i+5]}" ]] || \
|
|
[[ "${cur_group}" == 'none' && \
|
|
"${cur_dir}" != "${mods[i+2]}" ]]; then
|
|
output_header "$i"
|
|
cur_group="${mods[i+5]}"
|
|
cur_dir="${mods[i+2]}"
|
|
fi
|
|
local mod=${mods[i]%.lua}
|
|
local relstage=${mods[i+1]}
|
|
case ${relstage} in
|
|
stable )
|
|
out=''
|
|
;;
|
|
* )
|
|
out=${relstage}
|
|
;;
|
|
esac
|
|
printf "%-20s\t%s\n" "${mod}" "${out}" 1>&2
|
|
done
|
|
std::info ""
|
|
}
|
|
|
|
#......................................................................
|
|
human_readable_output() {
|
|
local -- cur_group=''
|
|
local -- cur_dir=''
|
|
local mod=''
|
|
local -i colsize=16
|
|
local -i column=$cols # force a line-break
|
|
for ((i=0; i<${#mods[@]}; i+=6)); do
|
|
# print header if
|
|
# - module is in another group or overlay
|
|
# - group == none && overlay == none and module is in another dir
|
|
if [[ "${cur_group}" != "${mods[i+5]}" ]] || \
|
|
[[ "${cur_group}" == 'none' && \
|
|
"${cur_dir}" != "${mods[i+2]}" ]]; then
|
|
output_header "$i"
|
|
cur_group="${mods[i+5]}"
|
|
cur_dir="${mods[i+2]}"
|
|
fi
|
|
if [[ ${Verbosity_lvl} == 'verbose' ]]; then
|
|
local relstage=${mods[i+1]}
|
|
case ${relstage} in
|
|
stable )
|
|
mod="${mods[i]%.lua}"
|
|
;;
|
|
* )
|
|
mod="${mods[i]%.lua}(${relstage:0:1})"
|
|
;;
|
|
esac
|
|
else
|
|
mod=${mods[i]}
|
|
fi
|
|
local -i len=${#mod}
|
|
if (( column+len >= cols )); then
|
|
printf -- "\n" 1>&2
|
|
column=0
|
|
fi
|
|
local -i size=0
|
|
(( size=((len)/colsize+1)*colsize ))
|
|
|
|
if (( column+size < cols )); then
|
|
printf "%-${size}s" "${mod}" 1>&2
|
|
else
|
|
printf "%-s" "${mod}" 1>&2
|
|
fi
|
|
column+=size
|
|
done
|
|
printf -- "\n\n" 1>&2
|
|
}
|
|
|
|
#......................................................................
|
|
local pattern=()
|
|
local output_function='human_readable_output'
|
|
local opt_use_relstages="${UsedReleaseStages}"
|
|
local -A opt_groups=()
|
|
local val=''
|
|
while (($# > 0)); do
|
|
case $1 in
|
|
-\? | -H | --help )
|
|
print_help "${SubCommand}"
|
|
;;
|
|
-a | --all | --all-release-stages )
|
|
opt_use_relstages="${ReleaseStages}"
|
|
;;
|
|
-h | --human )
|
|
output_function='human_readable_output'
|
|
;;
|
|
-l | --long )
|
|
output_function='long_output'
|
|
;;
|
|
-t | --terse )
|
|
output_function='terse_output'
|
|
;;
|
|
-m | --machine )
|
|
output_function='machine_output'
|
|
;;
|
|
-g | --group | --group=* )
|
|
if [[ $1 == --group=* ]]; then
|
|
val="${1#--*=}"
|
|
else
|
|
val="$2"
|
|
shift
|
|
fi
|
|
opt_groups[${val}]=1
|
|
;;
|
|
-- )
|
|
shift 1
|
|
pattern+=( "$@" )
|
|
break
|
|
;;
|
|
* )
|
|
pattern+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
if (( ${#pattern[@]} == 0 )); then
|
|
pattern+=( '' )
|
|
fi
|
|
|
|
# With overlays we can have multiple directories per group!
|
|
# To find the modules in a given group, we have to loop over
|
|
# these directories. In the for loop below, we create a
|
|
# 'modulepath' per group and a list of groups. We loop over
|
|
# this list of groups in the second for-loop.
|
|
local -- dir=''
|
|
local -- group=''
|
|
local -- groups=''
|
|
local -- ol=''
|
|
local -A modulepath_of_group=()
|
|
IFS=':' read -r -a modulepath <<<"${MODULEPATH}"
|
|
for dir in "${modulepath[@]}"; do
|
|
find_overlay ol group "${dir}" || group="${dir}"
|
|
[[ -v modulepath_of_group[${group}] ]] || modulepath_of_group[${group}]=''
|
|
typeset -n tmp="modulepath_of_group[${group}]"
|
|
std::append_path tmp "${dir}"
|
|
if (( ${#opt_groups[@]} > 0 )); then
|
|
# add only groups specified on the command line
|
|
[[ -v opt_groups[${group}] ]] || continue
|
|
fi
|
|
std::append_path groups "${group}"
|
|
done
|
|
local -a modulepath=()
|
|
for group in ${groups//:/ }; do
|
|
modulepath+="${modulepath_of_group[${group}]}:"
|
|
done
|
|
local -a path=()
|
|
IFS=':' read -r -a path <<<"${modulepath%:}"
|
|
for string in "${pattern[@]}"; do
|
|
get_available_modules \
|
|
'search' \
|
|
mods \
|
|
"${string}" \
|
|
"${opt_use_relstages}" \
|
|
"${path[@]}"
|
|
${output_function}
|
|
done
|
|
} # subcommand_avail()
|
|
|
|
##############################################################################
|
|
Subcommands['use']='use'
|
|
Options['use']='-l help -o \?Hap -l append -l prepend'
|
|
Help['use']="
|
|
USAGE:
|
|
module use [-a|--append|-p|--prepend] [directory|group|release_stage|...]
|
|
Without arguments this sub-command displays information about
|
|
the module search path, used groups and release stages. You can
|
|
use this sub-command to get a list of available groups and
|
|
releases stages.
|
|
|
|
With a directory as argument, this directory will either be
|
|
prepended or appended to the module search path. The default
|
|
is to prepend the directory.
|
|
|
|
With a group as argument, the modules in this group will
|
|
be made available.
|
|
|
|
With a release as argument, this modules with this release
|
|
will be made available.
|
|
|
|
SWITCHES:
|
|
-a | --append -p | --prepend )
|
|
Append/prepend agrument to module search path or list of to be
|
|
searched releases.
|
|
"
|
|
#..............................................................................
|
|
set_ol_modulepaths(){
|
|
local -r __doc__='
|
|
Prepend overlay specific directories to MODULEPATH.
|
|
If unstable is used:
|
|
prepend modulepath_unstable if not empty
|
|
otherwise prepend modulepath
|
|
else if only stable is used
|
|
prepend modulepath_stable if not empty
|
|
otherwise prepend modulepath
|
|
else if deprecated modules are available
|
|
prepend modulepath_deprecated if not empty
|
|
otherwise prepend modulepath.'
|
|
local -r ol_name="$1" # [in] name of overlay
|
|
local -- path=''
|
|
if [[ ":${UsedReleaseStages}:" == *:unstable:* ]]; then
|
|
path="${OverlayInfo[${ol_name}:modulepath_unstable]}"
|
|
elif [[ ":${UsedReleaseStages}:" == *:deprecated:* ]]; then
|
|
path="${OverlayInfo[${ol_name}:modulepath_deprecated]}"
|
|
else
|
|
path="${OverlayInfo[${ol_name}:modulepath_stable]}"
|
|
fi
|
|
[[ -z "${path}" ]] && path="${OverlayInfo[${ol_name}:modulepath]}"
|
|
[[ -z "${path}" ]] && return 0
|
|
|
|
local -a modulepath=()
|
|
IFS=':' read -r -a modulepath <<<"${path}"
|
|
local -- dir=''
|
|
for dir in "${modulepath[@]}"; do
|
|
std::prepend_path MODULEPATH "${dir}"
|
|
std::prepend_path ModulePathAppend "${dir}"
|
|
std::prepend_path ModulePathPrepend "${dir}"
|
|
done
|
|
}
|
|
|
|
#..............................................................................
|
|
unset_ol_modulepaths(){
|
|
local -r __doc__='Remove additional directories added by overlay'
|
|
local -r ol_name="$1" # [in] name of overlay
|
|
local -- path=''
|
|
if [[ -n "${OverlayInfo[${ol_name}:modulepath]}" ]]; then
|
|
path+="${OverlayInfo[${ol_name}:modulepath]}:"
|
|
fi
|
|
if [[ -n "${OverlayInfo[${ol_name}:modulepath_unstable]}" ]]; then
|
|
path+="${OverlayInfo[${ol_name}:modulepath_unstable]}:"
|
|
fi
|
|
if [[ -n "${OverlayInfo[${ol_name}:modulepath_stable]}" ]]; then
|
|
path+="${OverlayInfo[${ol_name}:modulepath_stable]}:"
|
|
fi
|
|
if [[ -n "${OverlayInfo[${ol_name}:modulepath_deprecated]}" ]]; then
|
|
path+="${OverlayInfo[${ol_name}:modulepath_deprecated]}:"
|
|
fi
|
|
[[ -z "${path}" ]] && return 0
|
|
|
|
path="${path%:}"
|
|
local -a modulepath=()
|
|
IFS=':' read -r -a modulepath <<<"${path}"
|
|
local -- dir=''
|
|
for dir in "${modulepath[@]}"; do
|
|
std::remove_path MODULEPATH "${dir}"
|
|
std::remove_path ModulePathAppend "${dir}"
|
|
std::remove_path ModulePathPrepend "${dir}"
|
|
done
|
|
}
|
|
|
|
#..............................................................................
|
|
subcommand_use() {
|
|
local -r __doc__='Implementation of the sub-command use.'
|
|
local -a modulepath=()
|
|
IFS=':' read -r -a modulepath <<<"${MODULEPATH}"
|
|
local add2path_func='std::append_path'
|
|
|
|
#......................................................................
|
|
group_is_used() {
|
|
[[ :${UsedGroups}: =~ :$1: ]]
|
|
}
|
|
|
|
#......................................................................
|
|
print_info() {
|
|
print_ol_info(){
|
|
local used="$1" # print used or unused overlays
|
|
local ol=''
|
|
for ol in "${Overlays[@]}"; do
|
|
[[ ${OverlayInfo[${ol}:used]} == "${used}" ]] || continue
|
|
local install_root="${OverlayInfo[${ol}:install_root]}"
|
|
local modulefiles_root="${OverlayInfo[${ol}:modulefiles_root]}"
|
|
local txt="\t${ol}"
|
|
if [[ ${install_root} == "${modulefiles_root}" ]]; then
|
|
txt+="\n\t\t${install_root}"
|
|
else
|
|
txt+="\n\t\t${install_root} (install root)"
|
|
txt+="\n\t\t${modulefiles_root} (modulefiles root)"
|
|
fi
|
|
case "${OverlayInfo[${ol}:type]}" in
|
|
"${ol_hiding}" )
|
|
txt+='\n\t\t(hiding modules with same name)'
|
|
;;
|
|
"${ol_replacing}" )
|
|
txt+='\n\t\t(replacing groups)'
|
|
;;
|
|
esac
|
|
std::info "${txt}"
|
|
done
|
|
}
|
|
|
|
local f
|
|
local r
|
|
std::info "Used groups:"
|
|
for f in ${UsedGroups//:/ }; do
|
|
std::info "\t${f}"
|
|
done
|
|
std::info ''
|
|
std::info "Unused groups:"
|
|
local _group
|
|
for _group in "${!GroupDepths[@]}"; do
|
|
local -i depth=${GroupDepths[${_group}]}
|
|
if ! group_is_used "${_group}" && (( depth == 0 )); then
|
|
std::info "\t${_group}"
|
|
fi
|
|
done
|
|
|
|
std::info "\nUsed releases stages:"
|
|
for r in ${UsedReleaseStages//:/ }; do
|
|
std::info "\t${r}"
|
|
done
|
|
std::info "\nUnused release stages:"
|
|
for r in ${ReleaseStages//:/ }; do
|
|
[[ ! ":${UsedReleaseStages}:" =~ :$r: ]] && std::info "\t${r}"
|
|
done
|
|
|
|
std::info ''
|
|
std::info "Used overlays:"
|
|
print_ol_info 'yes'
|
|
|
|
std::info ''
|
|
std::info "Unused overlays:"
|
|
print_ol_info 'no'
|
|
|
|
std::info ''
|
|
std::info "Additonal directories in MODULEPATH:"
|
|
local -i i=0
|
|
local -i n=0
|
|
local group
|
|
for (( i=0; i<${#modulepath[@]}; i++)); do
|
|
if ! find_overlay ol group "${modulepath[i]}"; then
|
|
std::info "\t${modulepath[i]}"
|
|
(( n+=1 ))
|
|
fi
|
|
done
|
|
(( n == 0 )) && std::info "\tnone"
|
|
std::info ""
|
|
} # print_info
|
|
|
|
#......................................................................
|
|
use () {
|
|
#..............................................................
|
|
use_relstage(){
|
|
local -r relstage="$1"
|
|
[[ ":${UsedReleaseStages}:" =~ :${relstage}: ]] && return 0
|
|
std::append_path UsedReleaseStages "${relstage}" || rc=$?
|
|
|
|
local -- ol_name=''
|
|
for ol_name in "${UsedOverlays[@]}"; do
|
|
[[ "${OverlayInfo[${ol_name}:has_additional_modulepaths]}" == 'false' ]] && \
|
|
continue
|
|
unset_ol_modulepaths "${ol_name}"
|
|
set_ol_modulepaths "${ol_name}"
|
|
done
|
|
}
|
|
#..............................................................
|
|
use_overlay() {
|
|
local ol_name="$1"
|
|
|
|
[[ -n "${LOADEDMODULES}" ]] && \
|
|
[[ "${LOADEDMODULES}" != Pmodules/+([.0-9rc]) ]] && \
|
|
die_ol_cannot_be_added "${ol_name}"
|
|
|
|
[[ ${OverlayInfo[${ol_name}:used]} == 'yes' ]] && return 0
|
|
|
|
local -a conflicts=( "${OverlayInfo[${ol_name}:conflicts]//:/ }" )
|
|
for ol in "${UsedOverlays[@]}"; do
|
|
for conflict in "${conflicts[@]}"; do
|
|
[[ "${ol}" == ${conflict} ]] && \
|
|
die_ol_conflict "${ol_name}" "${ol}"
|
|
done
|
|
done
|
|
|
|
if [[ "${OverlayInfo[${ol_name}:type]}" == "${ol_replacing}" ]]; then
|
|
# this overlay is masking groups. So, we have to remove
|
|
# the corresponding directories of the already used
|
|
# overlays from MODULEPATH.
|
|
for group in ${UsedGroups//:/ }; do
|
|
MaskedGroups[${group}]=''
|
|
local -- dir=''
|
|
# if this overlay has groups, we have to check whether
|
|
# ${group} exists in this overlay. If not, we have to
|
|
# do nothing.
|
|
# if this overlay doesn't have groups, we remove all
|
|
# groups.
|
|
if [[ "${OverlayInfo[${ol_name}:layout]}" == 'Pmodules' ]]; then
|
|
dir="${OverlayInfo[${ol_name}:modulefiles_root]}/"
|
|
dir+="${group}/${__MODULEFILES_DIR__}"
|
|
# is this group in this overlay?
|
|
[[ -d "${dir}" ]] || continue # no
|
|
fi
|
|
dir="/${group}/${__MODULEFILES_DIR__}"
|
|
local -a dirs=()
|
|
for ol in "${UsedOverlays[@]}"; do
|
|
dirs+=( "${OverlayInfo[${ol}:modulefiles_root]}${dir}" )
|
|
done
|
|
std::remove_path MODULEPATH "${dirs[@]}"
|
|
MaskedGroups[${group}]="${ol_name}:${MaskedGroups[${group}]}"
|
|
done
|
|
fi
|
|
if [[ -n "${OverlayInfo[${ol_name}:groups]}" ]]; then
|
|
local -- grp_changes=':'
|
|
local -a groups=()
|
|
IFS=':' read -r -a groups <<< "${OverlayInfo[${ol_name}:groups]}"
|
|
local -- group=''
|
|
for group in "${groups[@]}"; do
|
|
if [[ "${group:0:1}" == '~' ]]; then
|
|
if [[ ":${UsedGroups}:" == *:${group:1}:* ]]; then
|
|
subcommand_unuse "${group:1}"
|
|
grp_changes+="${group}:"
|
|
fi
|
|
else
|
|
if [[ ":${UsedGroups}:" != *:${group}:* ]]; then
|
|
use_group "${group}"
|
|
grp_changes+="${group}:"
|
|
fi
|
|
fi
|
|
done
|
|
OverlayInfo[${ol_name}:grp_changes]="${grp_changes:0: -1}"
|
|
fi
|
|
scan_groups "${ol_name}"
|
|
for group in ${UsedGroups//:/ }; do
|
|
local dir="${OverlayInfo[${ol_name}:modulefiles_root]}/"
|
|
dir+="${group}/${__MODULEFILES_DIR__}"
|
|
if [[ -d "${dir}" ]]; then
|
|
std::prepend_path MODULEPATH "${dir}"
|
|
fi
|
|
done
|
|
set_ol_modulepaths "${ol_name}"
|
|
UsedOverlays=( "${ol_name}" "${UsedOverlays[@]}" )
|
|
OverlayInfo[${ol_name}:used]='yes'
|
|
|
|
local -a excludes=()
|
|
IFS=':' read -r -a excludes <<< "${OverlayInfo[${ol_name}:excludes]}"
|
|
local -- item=''
|
|
for item in "${excludes[@]}"; do
|
|
OverlayExcludes+="${item}|"
|
|
done
|
|
if [[ -n "${OverlayExcludes}" ]]; then
|
|
OverlayExcludes="${OverlayExcludes:0: -1}"
|
|
fi
|
|
scan_groups "${UsedOverlays[@]}"
|
|
}
|
|
|
|
#..............................................................
|
|
use_group() {
|
|
local -- grp="$1"
|
|
# die if argument is a hierarchical group
|
|
(( ${GroupDepths[${grp}]} > 0 )) && \
|
|
die_grp_invalid "${grp}"
|
|
|
|
std::append_path UsedGroups "${grp}"
|
|
local -- ol_name
|
|
local -i i=0
|
|
local -i n="${#UsedOverlays[@]}"
|
|
for ((i=n-1; i>=0; i--)); do
|
|
ol_name="${UsedOverlays[i]}"
|
|
local dir="${OverlayInfo[${ol_name}:modulefiles_root]}/${grp}/${__MODULEFILES_DIR__}"
|
|
[[ -d "${dir}" ]] || continue
|
|
std::prepend_path MODULEPATH "${dir}"
|
|
[[ "${OverlayInfo[${ol_name}:type]}" == "${ol_replacing}" ]] && break
|
|
done
|
|
}
|
|
|
|
#..............................................................
|
|
local -- arg="$1"
|
|
local -i rc=0
|
|
if is_release_stage "${arg}"; then
|
|
use_relstage "${arg}" || rc=$?
|
|
return ${rc}
|
|
fi
|
|
if [[ -v OverlayInfo[${arg}:type] ]]; then
|
|
use_overlay "${arg}" || rc=$?
|
|
return ${rc}
|
|
fi
|
|
if [[ ! -v GroupDepths[${arg}] ]]; then
|
|
# this scan is required if a new group has been
|
|
# create inside an used overlay
|
|
scan_groups "${UsedOverlays[@]}"
|
|
fi
|
|
if [[ -v GroupDepths[${arg}] ]]; then
|
|
use_group "${arg}" || rc=$?
|
|
return ${rc}
|
|
fi
|
|
if [[ -d ${arg} ]]; then
|
|
local dir=''
|
|
dir=$(std::get_abspath "${arg}")
|
|
if [[ "${opt_append}" == 'yes' ]]; then
|
|
std::append_path MODULEPATH "${dir}" || rc=$?
|
|
std::append_path ModulePathAppend "${dir}" || rc=$?
|
|
else
|
|
std::prepend_path MODULEPATH "${dir}" || rc=$?
|
|
std::prepend_path ModulePathPrepend "${dir}" || rc=$?
|
|
fi
|
|
return ${rc}
|
|
fi
|
|
die_args_invalid_for_subcmd_use "${arg}"
|
|
} # use ()
|
|
|
|
#......................................................................
|
|
local -a args=()
|
|
opt_append='yes'
|
|
while (( $# > 0)); do
|
|
case "$1" in
|
|
-\? | -H | --help )
|
|
print_help "${SubCommand}"
|
|
;;
|
|
-a | --append )
|
|
opt_append='yes'
|
|
;;
|
|
-p | --prepend )
|
|
opt_append='no'
|
|
;;
|
|
-- )
|
|
shift 1
|
|
args+=( "$@" )
|
|
break
|
|
;;
|
|
* )
|
|
args+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
if (( ${#args[@]} == 0 )); then
|
|
print_info
|
|
return
|
|
fi
|
|
for arg in "${args[@]}"; do
|
|
use "${arg}"
|
|
done
|
|
vars_to_be_exported['PMODULES_ENV']=1
|
|
vars_to_be_exported['MODULEPATH']=1
|
|
}
|
|
|
|
##############################################################################
|
|
Subcommands['unuse']='unuse'
|
|
Options['unuse']='-o \?H -l help'
|
|
Help['unuse']='
|
|
unuse directory|group|release...
|
|
Remove the given modulefiles directory, group, release stage,
|
|
flag from the search path.
|
|
'
|
|
#..............................................................................
|
|
subcommand_unuse() {
|
|
local -r __doc__='Implementation of the sub-command unuse.'
|
|
local -a modulepath=()
|
|
IFS=':' read -r -a modulepath <<<"${MODULEPATH}"
|
|
|
|
#......................................................................
|
|
unuse() {
|
|
unuse_relstage() {
|
|
local -r relstage="$1"
|
|
|
|
[[ ! ":${UsedReleaseStages}:" =~ :${relstage}: ]] && return 0
|
|
std::remove_path UsedReleaseStages "${relstage}"
|
|
|
|
local -- ol_name=''
|
|
for ol_name in "${UsedOverlays[@]}"; do
|
|
[[ "${OverlayInfo[${ol_name}:has_additional_modulepaths]}" == 'false' ]] && \
|
|
continue
|
|
unset_ol_modulepaths "${ol_name}"
|
|
set_ol_modulepaths "${ol_name}"
|
|
done
|
|
}
|
|
|
|
#..............................................................
|
|
unuse_overlay() {
|
|
local ol_name="$1"
|
|
|
|
if [[ -n "${LOADEDMODULES}" ]] && \
|
|
[[ "${LOADEDMODULES}" != Pmodules/+([.0-9rc]) ]]; then
|
|
die_ol_cannot_be_removed
|
|
fi
|
|
|
|
[[ "${ol_name}" == 'base' ]] && \
|
|
die_ol_cannot_remove_base
|
|
[[ "${OverlayInfo[${ol_name}:used]}" != 'yes' ]] && \
|
|
die_ol_not_used "${ol_name}"
|
|
# make sure first index is '0' (it should, but you never know)
|
|
UsedOverlays=( "${UsedOverlays[@]}" )
|
|
[[ "${ol_name}" != "${UsedOverlays[0]}" ]] && \
|
|
die_ol_not_on_top_of_stack "${ol_name}"
|
|
|
|
OverlayInfo[${ol_name}:used]='no'
|
|
UsedOverlays=( "${UsedOverlays[@]:1}")
|
|
|
|
if [[ "${OverlayInfo[${ol_name}:type]}" == "${ol_replacing}" ]]; then
|
|
# if this overlay masks groups, we have to re-add
|
|
# some directories to MODULEPATH.
|
|
for group in ${UsedGroups//:/ }; do
|
|
local -a overlays=( ${MaskedGroups[${group}]//:/ } )
|
|
(( ${#overlays[@]} == 0)) && continue
|
|
[[ "${ol_name}" != "${overlays[0]}" ]] && continue
|
|
# remove overlay "${ol_name}"
|
|
local -- s
|
|
printf -v s "%s:" "${overlays[@]:1}"
|
|
MaskedGroups[${group}]="$s"
|
|
|
|
local -- ol=''
|
|
local -- dir=''
|
|
if (( ${#overlays[@]} == 1 )); then
|
|
# Only overlay ${ol_name} is masking this group.
|
|
# Add the group directories for all overlays.
|
|
for ol in "${UsedOverlays[@]}"; do
|
|
dir="${OverlayInfo[${ol}:modulefiles_root]}/"
|
|
dir+="${group}/${__MODULEFILES_DIR__}"
|
|
[[ -d "${dir}" ]] || continue
|
|
std::prepend_path MODULEPATH "${dir}"
|
|
done
|
|
continue
|
|
fi
|
|
# There is at least one more overlay masking this group.
|
|
# If this overlay has no groups, we have to do nothing.
|
|
ol="${overlays[1]}"
|
|
[[ "${OverlayInfo[${ol}:layout]}" != 'Pmodules' ]] && continue
|
|
# add group directory for this overlay, if exists
|
|
dir="${OverlayInfo[${ol}:modulefiles_root]}/"
|
|
dir+="${group}/${__MODULEFILES_DIR__}"
|
|
[[ -d "${dir}" ]] || continue
|
|
std::prepend_path MODULEPATH "${dir}"
|
|
done
|
|
fi
|
|
if [[ -n "${OverlayInfo[${ol_name}:groups]}" ]]; then
|
|
local -a groups=()
|
|
IFS=':' read -r -a groups <<< "${OverlayInfo[${ol_name}:grp_changes]}"
|
|
local -- group=''
|
|
for group in "${groups[@]}"; do
|
|
if [[ "${group:0:1}" == '~' ]]; then
|
|
if [[ ":${UsedGroups}:" != *:${group:1}:* ]]; then
|
|
subcommand_use "${group:1}"
|
|
fi
|
|
else
|
|
if [[ ":${UsedGroups}:" == *:${group}:* ]]; then
|
|
unuse_group "${group}"
|
|
fi
|
|
fi
|
|
done
|
|
OverlayInfo[${ol_name}:grp_changes]=':'
|
|
fi
|
|
|
|
# rebuild exclude list.
|
|
# Note:
|
|
# A module might be excluded in multiple overlays. So, we cannot
|
|
# just remove the excludes from the overlay to unuse.
|
|
OverlayExcludes=''
|
|
local -- ol=''
|
|
local -a excludes=()
|
|
local -- item=''
|
|
for ol in "${UsedOverlays[@]}"; do
|
|
IFS=':' read -r -a excludes <<< "${OverlayInfo[${ol}:excludes]}"
|
|
for item in "${excludes[@]}"; do
|
|
OverlayExcludes+="${item}|"
|
|
done
|
|
done
|
|
if [[ -n "${OverlayExcludes}" ]]; then
|
|
OverlayExcludes="${OverlayExcludes:0: -1}"
|
|
fi
|
|
unset_ol_modulepaths "${ol_name}"
|
|
|
|
# remove root of overlay
|
|
local dir
|
|
for dir in "${modulepath[@]}"; do
|
|
[[ "${dir}" == "${OverlayInfo[${ol_name}:modulefiles_root]}" ]] && \
|
|
std::remove_path MODULEPATH "${dir}"
|
|
|
|
done
|
|
vars_to_be_exported['PMODULES_ENV']=1
|
|
}
|
|
|
|
#..............................................................
|
|
unuse_group() {
|
|
local -- grp="$1"
|
|
(( ${GroupDepths[${grp}]} > 0 )) && \
|
|
die_grp_invalid "${grp}"
|
|
|
|
if [[ -v PMODULES_LOADED_${grp^^} ]]; then
|
|
local var="PMODULES_LOADED_${grp^^}"
|
|
[[ -n "${!var}" ]] && \
|
|
die_grp_cannot_be_removed "${grp}"
|
|
fi
|
|
std::remove_path UsedGroups "${grp}"
|
|
local overlay
|
|
for overlay in "${UsedOverlays[@]}"; do
|
|
local dir="${OverlayInfo[${overlay}:modulefiles_root]}"
|
|
dir+="/${grp}/${__MODULEFILES_DIR__}"
|
|
std::remove_path MODULEPATH "${dir}"
|
|
done
|
|
}
|
|
|
|
#..............................................................
|
|
local arg=$1
|
|
|
|
if is_release_stage "${arg}"; then
|
|
unuse_relstage "${arg}"
|
|
return 0
|
|
fi
|
|
if [[ -v OverlayInfo[${arg}:type] ]]; then
|
|
unuse_overlay "${arg}"
|
|
return 0
|
|
fi
|
|
if [[ -d ${arg} ]]; then
|
|
local dir=''
|
|
dir=$(std::get_abspath "${arg}")
|
|
std::remove_path MODULEPATH "${dir}"
|
|
std::remove_path ModulePathAppend "${dir}"
|
|
std::remove_path ModulePathPrepend "${dir}"
|
|
return 0
|
|
fi
|
|
if [[ -v GroupDepths[${arg}] ]]; then
|
|
unuse_group "${arg}"
|
|
return 0
|
|
fi
|
|
die_args_invalid_for_subcmd_use "${arg}"
|
|
} # unuse()
|
|
|
|
#......................................................................
|
|
local -a args=()
|
|
while (( $# > 0)); do
|
|
case "$1" in
|
|
-\? | -H | --help )
|
|
print_help "${SubCommand}"
|
|
;;
|
|
-- )
|
|
shift 1
|
|
args+=( "$@" )
|
|
break
|
|
;;
|
|
* )
|
|
args+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
(( ${#args[@]} == 0 )) && die_args_missing
|
|
for arg in "${args[@]}"; do
|
|
unuse "${args[@]}"
|
|
done
|
|
vars_to_be_exported['PMODULES_ENV']=1
|
|
vars_to_be_exported['MODULEPATH']=1
|
|
}
|
|
|
|
##############################################################################
|
|
Subcommands['update']='update'
|
|
Options['update']='-o \?H -l help'
|
|
Help['update']='
|
|
USAGE:
|
|
module update
|
|
Attempt to reload all loaded modulefiles.
|
|
'
|
|
#..............................................................................
|
|
subcommand_update() {
|
|
local -r __doc__'
|
|
Implementation of the sub-command update
|
|
:FIXME:
|
|
Either compile Modules with --enable-beginenv or remove the
|
|
sub-command'
|
|
subcommand_generic0 "$@"
|
|
}
|
|
|
|
##############################################################################
|
|
Subcommands['refresh']='refresh'
|
|
Options[refresh]='-o \?H -l help'
|
|
Help[refresh]='
|
|
USAGE:
|
|
module refresh
|
|
Force a refresh of all non-persistent components of currently
|
|
loaded modules. This should be used on derived shells where
|
|
aliases need to be reinitialized but the environment variables
|
|
have already been set by the currently loaded modules.
|
|
'
|
|
#..............................................................................
|
|
subcommand_refresh() {
|
|
local -r __doc__='Implementation of the sub-command refresh.'
|
|
subcommand_generic0 "$@"
|
|
}
|
|
|
|
#..............................................................................
|
|
pmodules_setup() {
|
|
local -r __doc__='Setup/initialize Pmodules environment.'
|
|
local -r mode="${1:-check}"
|
|
init_used_groups() {
|
|
declare -gx UsedGroups=''
|
|
local group
|
|
for group in ${DefaultGroups//:/ }; do
|
|
std::append_path UsedGroups "${group}"
|
|
done
|
|
vars_to_be_exported['PMODULES_ENV']=1
|
|
}
|
|
init_used_releases() {
|
|
declare -g UsedReleaseStages=''
|
|
for r in ${DefaultReleaseStages//:/ }; do
|
|
std::append_path UsedReleaseStages "${r}"
|
|
done
|
|
vars_to_be_exported['PMODULES_ENV']=1
|
|
}
|
|
init_overlay_vars() {
|
|
declare -agx UsedOverlays=( 'base' )
|
|
OverlayInfo['base:used']='yes'
|
|
declare -g OverlayExcludes=''
|
|
scan_groups "${UsedOverlays[@]}"
|
|
vars_to_be_exported['PMODULES_ENV']=1
|
|
}
|
|
|
|
init_modulepath() {
|
|
declare -gx MODULEPATH=''
|
|
local group
|
|
local ol
|
|
for ol in "${UsedOverlays[@]}"; do
|
|
for group in ${UsedGroups//:/ }; do
|
|
local dir="${OverlayInfo[${ol}:modulefiles_root]}"
|
|
dir+="/${group}/${__MODULEFILES_DIR__}"
|
|
if [[ -d "${dir}" ]]; then
|
|
std::prepend_path MODULEPATH "${dir}"
|
|
fi
|
|
done
|
|
done
|
|
vars_to_be_exported['MODULEPATH']=1
|
|
}
|
|
|
|
init_manpath() {
|
|
: "
|
|
Initialize MANPATH. Use systen wide configuration file
|
|
if exist or set some defaults.
|
|
|
|
"
|
|
std::remove_path MANPATH "${PMODULES_HOME%/*}/.*"
|
|
|
|
if [[ -r /etc/man.config ]]; then
|
|
declare _manconf='/etc/man.config'
|
|
elif [[ -r /etc/man.conf ]]; then
|
|
declare _manconf='/etc/man.conf'
|
|
fi
|
|
if [[ -v _manconf ]]; then
|
|
# initialize from system configuration file
|
|
while read -r name value rest; do
|
|
std::append_path MANPATH "${value}"
|
|
done < <(grep "^MANPATH\s" "${_manconf}")
|
|
unset _manconf
|
|
else
|
|
# set defaults
|
|
std::append_path MANPATH "${PMODULES_HOME}/share/man"
|
|
std::append_path MANPATH "/usr/share/man"
|
|
fi
|
|
vars_to_be_exported['MANPATH']=1
|
|
}
|
|
|
|
init_load_modules(){
|
|
declare -gx LOADEDMODULES=''
|
|
declare -gx _LMFILES_=''
|
|
}
|
|
|
|
# We (re-)initialise the Pmodules system, if
|
|
# PMODULES_ENV was not set/is empty
|
|
[[ -v PMODULES_ENV ]] || PMODULES_ENV=''
|
|
if [[ -z ${PMODULES_ENV} || ${mode} = 'init' ]]; then
|
|
pm::read_config
|
|
init_load_modules
|
|
init_used_groups
|
|
init_used_releases
|
|
init_overlay_vars
|
|
scan_groups "${UsedOverlays[@]}"
|
|
init_modulepath
|
|
init_manpath
|
|
else
|
|
# restore variables from last call
|
|
eval "$("${base64}" -d <<< "${PMODULES_ENV}" 2>/dev/null)"
|
|
if (( ${#UsedOverlays[@]} == 0 )); then
|
|
pm::read_config
|
|
fi
|
|
[[ -z "${LOADEDMODULES}" || -z ${_LMFILES_} ]] && init_load_modules
|
|
[[ -z "${UsedGroups}" ]] && init_used_groups
|
|
[[ -z "${UsedReleaseStages}" ]] && init_used_releases
|
|
(( ${#UsedOverlays[@]} == 0 )) && init_overlay_vars
|
|
(( ${#GroupDepths[@]} == 0 )) && scan_groups "${UsedOverlays[@]}"
|
|
[[ -z "${MODULEPATH}" ]] && init_modulepath
|
|
[[ -z "${MANPATH}" ]] && init_manpath
|
|
fi
|
|
[[ -z Version ]] || Version='0.0.0'
|
|
if [[ "${Version}" != "${PMODULES_VERSION}" ]]; then
|
|
Version="${PMODULES_VERSION}"
|
|
vars_to_be_exported[PMODULES_HOME]=1
|
|
fi
|
|
save_env
|
|
}
|
|
|
|
##############################################################################
|
|
Subcommands['purge']='purge'
|
|
Options['purge']='-o \?H -l help'
|
|
Help['purge']='
|
|
USAGE:
|
|
module purge
|
|
Unload all loaded modulefiles.
|
|
'
|
|
#..............................................................................
|
|
subcommand_purge() {
|
|
local -r __doc__='
|
|
Implementation of the sub-command purge.
|
|
Note:
|
|
If a Pmodule module is loaded, it will *not* be
|
|
unloaded!'
|
|
local -a args=()
|
|
while (( $# > 0)); do
|
|
case "$1" in
|
|
-\? | -H | --help )
|
|
print_help "${SubCommand}"
|
|
;;
|
|
-- )
|
|
shift 1
|
|
args+=( "$@" )
|
|
break
|
|
;;
|
|
-* )
|
|
die_args_invalid_opt "$1"
|
|
;;
|
|
* )
|
|
args+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
(( ${#args[@]} > 0 )) && die_args_not_allowed
|
|
|
|
# unload all modules (except Pmodules itself)
|
|
IFS=':' read -r -a modules <<< "${LOADEDMODULES}"
|
|
for (( i=${#modules[@]}-1; i>=0; i-- )); do
|
|
[[ ${modules[$i]} == Pmodules/* ]] && continue
|
|
subcommand_unload "${modules[$i]}"
|
|
done
|
|
# re-initialize Pmodules
|
|
# - save
|
|
# - used release stages
|
|
local -- saved_relstages="${UsedReleaseStages}"
|
|
# - used groups
|
|
local -- saved_groups="${UsedGroups}"
|
|
# - used overlays
|
|
local -- saved_overlays=''
|
|
printf -v saved_overlays "%s:" "${UsedOverlays[@]}"
|
|
saved_overlays="${saved_overlays%:}"
|
|
# - additional directories in MODULEPATH
|
|
local -- item=''
|
|
local -- group=''
|
|
local -a items=()
|
|
local -- saved_modulepath=''
|
|
IFS=':' read -r -a items <<<"${MODULEPATH}"
|
|
for item in "${items[@]}"; do
|
|
find_overlay ol group "${item}" && continue
|
|
saved_modulepath+="${item}:"
|
|
done
|
|
|
|
# - re-initialize PMODULES_ENV
|
|
pmodules_setup init
|
|
|
|
# - restore
|
|
IFS=':' read -r -a items <<<"${saved_relstages}"
|
|
for item in "${items[@]}"; do
|
|
subcommand_use "${item}"
|
|
done
|
|
IFS=':' read -r -a items <<<"${saved_overlays}"
|
|
for item in "${items[@]}"; do
|
|
[[ -v OverlayInfo[${item}:used] ]] || continue
|
|
subcommand_use "${item}"
|
|
done
|
|
IFS=':' read -r -a items <<<"${saved_groups}"
|
|
for item in "${items[@]}"; do
|
|
[[ -v GroupDepths[${item}] ]] || continue
|
|
subcommand_use "${item}"
|
|
done
|
|
IFS=':' read -r -a items <<<"${saved_modulepath}"
|
|
for item in "${items[@]}"; do
|
|
[[ -d "${item}" ]] || continue
|
|
subcommand_use "${item}"
|
|
done
|
|
}
|
|
|
|
##############################################################################
|
|
Subcommands['list']='list'
|
|
Options['list']='-l help -o \?Hhlt -l human -l long -l terse'
|
|
Help['list']='
|
|
USAGE:
|
|
module list
|
|
List loaded modules.
|
|
'
|
|
#..............................................................................
|
|
subcommand_list() {
|
|
local -r __doc__='Implementation of the sub-command list.'
|
|
human_readable_output(){
|
|
# get list of loaded modules with stripped MODULEPATH
|
|
IFS=':' read -r -a modules \
|
|
< <( ${sed} "s;${MODULEPATH//:/\/\\\|}/;;g" <<< "${_LMFILES_}" )
|
|
local -- strs=()
|
|
local -i n=1 # enumeration of loaded modules
|
|
local -i colsize=0
|
|
local -- s=''
|
|
for module in "${modules[@]%.lua}"; do
|
|
s=$(printf "%2d) %-s" "$n" "${module}")
|
|
strs+=( "$s" )
|
|
(( ${#s} > colsize )) && (( colsize=${#s} ))
|
|
(( n+=1 ))
|
|
done
|
|
(( colsize+=2 ))
|
|
|
|
local -i cols=80
|
|
[[ -t 1 && -t 2 ]] && cols=$(${tput} cols)
|
|
local -i column=0
|
|
printf "Currently Loaded Modules:\n" 1>&2
|
|
for s in "${strs[@]}"; do
|
|
local -i len=${#s}
|
|
if (( column+len >= cols )); then
|
|
printf -- "\n" 1>&2
|
|
column=0
|
|
fi
|
|
if (( column+colsize < cols )); then
|
|
printf "%-${colsize}s" "$s" 1>&2
|
|
else
|
|
printf "%-s" "$s" 1>&2 # last column
|
|
fi
|
|
(( column+=colsize ))
|
|
done
|
|
printf -- "\n\n" 1>&2
|
|
}
|
|
|
|
long_output(){
|
|
IFS=':' read -r -a modules \
|
|
< <( ${sed} "s;${MODULEPATH//:/\/\\\|}/;;g" <<< "${_LMFILES_}" )
|
|
IFS=':' read -r -a lmfiles <<< "${_LMFILES_}"
|
|
printf "Currently Loaded Modules:\n" 1>&2
|
|
local -i fmt_field_width=0
|
|
local -i length=0
|
|
local module
|
|
for module in "${modules[@]}"; do
|
|
length=${#module}
|
|
(( length > fmt_field_width )) && fmt_field_width=length
|
|
done
|
|
for (( i=0; i<${#lmfiles[@]}; i++ )); do
|
|
mtime=$(date -r "${lmfiles[i]}" +"%Y-%m-%d %H:%M:%S")
|
|
printf "%-${fmt_field_width}s\t%s\n" "${modules[i]}" "${mtime}" 1>&2
|
|
done
|
|
}
|
|
|
|
terse_output(){
|
|
IFS=':' read -r -a modules \
|
|
< <( ${sed} "s;${MODULEPATH//:/\/\\\|}/;;g" <<< "${_LMFILES_}" )
|
|
printf "Currently Loaded Modules:\n" 1>&2
|
|
for module in "${modules[@]%.lua}"; do
|
|
printf "%s\n" "${module}" 1>&2
|
|
done
|
|
printf -- "\n\n" 1>&2
|
|
}
|
|
|
|
local args=()
|
|
local output_function='human_readable_output'
|
|
while (( $# > 0 )); do
|
|
case $1 in
|
|
-\? | -H | --help )
|
|
print_help "${SubCommand}"
|
|
;;
|
|
-h | --human )
|
|
output_function='human_readable_output'
|
|
;;
|
|
-l | --long )
|
|
output_function='long_output'
|
|
;;
|
|
-t | --terse )
|
|
output_function='terse_output'
|
|
;;
|
|
-- )
|
|
shift 1
|
|
args+=( "$@" )
|
|
break
|
|
;;
|
|
-* )
|
|
die_args_invalid_opt "$1"
|
|
;;
|
|
* )
|
|
args+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
(( ${#args[@]} > 0 )) && \
|
|
die_args_not_allowed
|
|
|
|
"${output_function}"
|
|
}
|
|
|
|
##############################################################################
|
|
Subcommands['clear']='clear'
|
|
Options['clear']='-o \?H -l help'
|
|
Help['clear']='
|
|
USAGE:
|
|
module clear
|
|
Force the Modules package to believe that no modules are
|
|
currently loaded.
|
|
'
|
|
#..............................................................................
|
|
subcommand_clear() {
|
|
local -r __doc__='Implementation of the sub-command clear.'
|
|
local -a args=()
|
|
while (( $# > 0 )); do
|
|
case $1 in
|
|
-\? | -H | --help )
|
|
print_help "${SubCommand}"
|
|
;;
|
|
-- )
|
|
shift 1
|
|
args+=( "$@" )
|
|
break
|
|
;;
|
|
* )
|
|
args+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
(( ${#args[@]} > 0 )) && die_args_not_allowed
|
|
|
|
pmodules_setup 'init'
|
|
}
|
|
|
|
##############################################################################
|
|
Subcommands['search']='search'
|
|
Subcommands['find']='search'
|
|
Subcommands['spider']='search'
|
|
Options['search']='-o a\?H -l help -l no-header -l print-modulefiles '
|
|
Options['search']+='-l release-stage: -l with: -l all-release-stages -l src: -l print-csv '
|
|
Options['search']+='-l verbose '
|
|
Options['search']+='-l all-deps -l wrap '
|
|
Options['search']+='-l glob '
|
|
Options['search']+='-l newest '
|
|
Options['search']+='-l group:'
|
|
Help['search']='
|
|
USAGE:
|
|
module find|search|spider [switches] STRING...
|
|
Search installed modules. If an argument is given, search
|
|
for modules whose name match the argument.
|
|
|
|
SWITCHES:
|
|
-a|--all-release-stages
|
|
Search within all releases.
|
|
|
|
--all-deps
|
|
Show all dependecies
|
|
|
|
--glob
|
|
Interpret STRING as shell pattern.
|
|
|
|
--no-header
|
|
Suppress output of a header.
|
|
|
|
--release-stage=RELEASE_STAGE
|
|
Search for modules within this release stage. You can specify
|
|
this switch multiple times. Without this switch, the release
|
|
stages in use will be searched.
|
|
|
|
--verbose
|
|
vebose output
|
|
|
|
--with=STRING
|
|
Search for modules compiled with modules matching string. The
|
|
command
|
|
|
|
module search --with=gcc/4.8.3
|
|
|
|
lists all modules in the hierarchy compiled with gcc 4.8.3.
|
|
|
|
--wrap
|
|
wrap output
|
|
'
|
|
#..............................................................................
|
|
subcommand_search() {
|
|
local -r __doc__='Implementation of the sub-command search.'
|
|
local -a modules=()
|
|
local -a groups=()
|
|
local -- group=''
|
|
local -- with_modules='//'
|
|
local -i cols=80
|
|
[[ -t 1 && -t 2 ]] && cols=$(${tput} cols) # get number of columns of terminal
|
|
local -i max_len_modulename=0
|
|
local opt_print_header='yes'
|
|
local opt_print_modulefiles='no'
|
|
local opt_print_csv='no'
|
|
local opt_print_verbose='no'
|
|
local opt_use_relstages=':'
|
|
local opt_all_deps='no'
|
|
local opt_wrap='no'
|
|
local opt_newest='no'
|
|
local -- opt_print_raw='no'
|
|
local -A spider_keys=()
|
|
local -- spider_output=''
|
|
|
|
#.....................................................................
|
|
#
|
|
# output result of search
|
|
# Args:
|
|
# $1: tmp file
|
|
#
|
|
# variables used from enclosing function:
|
|
# opt_print_header
|
|
# opt_print_modulefiles
|
|
# with_modules
|
|
#
|
|
print_result() {
|
|
local func_print_header=''
|
|
local func_print_line=''
|
|
local fmt=''
|
|
|
|
print_default() {
|
|
fmt="%-${max_len_modulename}s %-10s %-12s %-12s %-s"
|
|
if [[ ${opt_print_header} == 'yes' ]]; then
|
|
func_print_header='print_header_default'
|
|
else
|
|
func_print_header='print_header_none'
|
|
fi
|
|
func_print_line='print_line_default'
|
|
}
|
|
|
|
print_header_default() {
|
|
std::info ''
|
|
std::info "${fmt}" "Module" "Rel.stage" "Group" "Overlay" "Requires"
|
|
std::info '-%.0s' $(seq 1 "${cols}")
|
|
}
|
|
|
|
print_line_default() {
|
|
write_line() {
|
|
local str="$1"
|
|
if [[ -t 1 && -t 2 ]] && (( ${#str} >= cols )); then
|
|
str="${str:0:$((cols-1))}>"
|
|
fi
|
|
std::info "${str}"
|
|
}
|
|
local -a deps=( "${@:6}" )
|
|
(( ${#deps[@]} == 0 )) && deps[0]=''
|
|
local -- str=''
|
|
if [[ "${opt_wrap}" == 'no' ]]; then
|
|
str=$(printf "${fmt}" "${1/.lua}" "$2" "$3" "$5" "${deps[*]}")
|
|
write_line "${str}"
|
|
else
|
|
str=$(printf "${fmt}" "${1/.lua}" "$2" "$3" "$5" "${deps[0]}")
|
|
for (( i = 1; i < ${#deps[@]}; i++ )); do
|
|
if (( ${#str} + ${#deps[i]} + 1 <= cols )); then
|
|
str+=" ${deps[i]}"
|
|
else
|
|
write_line "${str}"
|
|
str=$(printf "${fmt}" "" "" "" "> ${deps[i]}")
|
|
fi
|
|
done
|
|
write_line "${str}"
|
|
fi
|
|
}
|
|
|
|
print_verbose() {
|
|
fmt="%-${max_len_modulename}s %-12s %-14s %-s"
|
|
func_print_header='print_header_verbose'
|
|
func_print_line='print_line_verbose'
|
|
}
|
|
|
|
print_header_verbose() {
|
|
:
|
|
}
|
|
|
|
print_line_verbose() {
|
|
local deps="${*:6}"
|
|
[[ -z ${deps} ]] && deps="(none)"
|
|
std::info "$1:"
|
|
std::info " release stage: $2"
|
|
std::info " group: $3"
|
|
std::info " overlay: $5"
|
|
std::info " modulefile: $4"
|
|
std::info " dependencies: ${deps}"
|
|
}
|
|
|
|
# print full modulefile names only
|
|
print_modulefiles() {
|
|
fmt=''
|
|
func_print_header='print_header_none'
|
|
func_print_line='print_line_modulefile'
|
|
}
|
|
|
|
print_header_none() {
|
|
:
|
|
}
|
|
|
|
print_line_modulefile() {
|
|
if (( $# >= 4 )) && [[ -n $4 ]]; then
|
|
std::info "$1 $4"
|
|
fi
|
|
}
|
|
|
|
print_line_raw() {
|
|
echo "$@"
|
|
}
|
|
|
|
print_raw() {
|
|
fmt=''
|
|
func_print_header='print_header_none'
|
|
func_print_line='print_line_raw'
|
|
}
|
|
|
|
if [[ "${opt_print_modulefiles}" == 'yes' ]]; then
|
|
print_modulefiles
|
|
elif [[ "${opt_print_csv}" == 'yes' ]]; then
|
|
print_csv
|
|
elif [[ "${opt_print_verbose}" == 'yes' ]]; then
|
|
print_verbose
|
|
elif [[ "${opt_print_raw}" == 'yes' ]]; then
|
|
print_raw
|
|
else
|
|
print_default
|
|
fi
|
|
|
|
local _script=''
|
|
if [[ ${opt_newest} == 'yes' ]]; then
|
|
_script='{} END{print}'
|
|
fi
|
|
${func_print_header}
|
|
local -a toks=()
|
|
while read -r -a toks; do
|
|
${func_print_line} "${toks[@]}"
|
|
done < <("${sort}" --version-sort -k 1,1 -k 6,6 -k 7,7 "${TmpFile}" | \
|
|
${awk} "${with_modules} ${_script}")
|
|
}
|
|
|
|
#.....................................................................
|
|
#
|
|
# search modules
|
|
#
|
|
search () {
|
|
local -r module="$1" # name/pattern to search
|
|
shift 1
|
|
local -a modulepath=("$@") # directories to search
|
|
local -i depth=0
|
|
|
|
# get and print all available modules in $mpath
|
|
# with respect to the requested release stage
|
|
# TmpFile: module/version relstage group dependencies...
|
|
local -a mods=()
|
|
get_available_modules \
|
|
'search' \
|
|
mods \
|
|
"${module}" \
|
|
"${opt_use_relstages}" \
|
|
"${modulepath[@]}"
|
|
local i=0
|
|
for (( i=0; i<${#mods[@]}; i+=6 )); do
|
|
local -- name="${mods[i]}"
|
|
local -- relstage="${mods[i+1]}"
|
|
local -- moduledir="${mods[i+2]}"
|
|
local -- rel_modulefile="${mods[i+3]}"
|
|
local -- modulefile="${moduledir}/${rel_modulefile}"
|
|
local -- ol="${mods[i+4]}"
|
|
local -- group="${mods[i+5]}"
|
|
local -a deps=()
|
|
|
|
if (( ${#name} > max_len_modulename)); then
|
|
max_len_modulename=${#name}
|
|
fi
|
|
if [[ "${OverlayInfo[${ol}:layout]}" == 'Pmodules' ]]; then
|
|
if [[ "${opt_print_verbose}" == 'yes' ]] || \
|
|
[[ "${opt_all_deps}" == 'yes' ]]; then
|
|
local prefix=''
|
|
get_module_prefix prefix "${modulefile}"
|
|
local dependencies_file="${prefix}/.dependencies"
|
|
if [[ -n ${prefix} ]] && [[ -r "${dependencies_file}" ]]; then
|
|
mapfile -t deps < "${dependencies_file}"
|
|
fi
|
|
else
|
|
# get dependencies encoded in directory name
|
|
local -i j
|
|
local -a toks
|
|
IFS='/'
|
|
read -r -a toks <<< "${modulefile}"
|
|
local -i depth=${GroupDepths[${group}]}
|
|
for ((j = -depth-2; j < -2; j += 2)); do
|
|
deps+=( "${toks[*]: $j:2}" );
|
|
done
|
|
unset IFS
|
|
fi
|
|
echo "${name}" "${relstage}" "${group}" "${modulefile}" \
|
|
"${ol}" \
|
|
"${deps[@]}" >> "${TmpFile}"
|
|
continue
|
|
fi
|
|
if [[ "${modulefile}" == *.lua ]] && \
|
|
[[ -v spider_keys[${modulefile}] ]]; then
|
|
local -i j=0
|
|
local -i n=0
|
|
local -- tmp_str=''
|
|
n=$( ${yq} ".\"${modulefile}\".parentAA|length" <<<"${spider_output}" )
|
|
for (( j=0; j<n; j++ )); do
|
|
tmp_str="$(${yq} ".\"${modulefile}\".parentAA[$j][]" <<<"${spider_output}" )"
|
|
readarray -t deps <<<"${tmp_str}"
|
|
echo "${name}" "${relstage}" "${group}" "${modulefile}" \
|
|
"${ol}" \
|
|
"${deps[@]}" >> "${TmpFile}"
|
|
done
|
|
continue
|
|
fi
|
|
echo "${name}" "${relstage}" "${group}" "${modulefile}" \
|
|
"${ol}" \
|
|
"${deps[@]}" >> "${TmpFile}"
|
|
done
|
|
}
|
|
|
|
while (( $# > 0 )); do
|
|
case $1 in
|
|
-\? | -H | --help )
|
|
print_help "${SubCommand}"
|
|
;;
|
|
--all-deps )
|
|
opt_all_deps='yes'
|
|
;;
|
|
--no-header )
|
|
opt_print_header='no'
|
|
;;
|
|
--print-modulefiles )
|
|
opt_print_modulefiles='yes'
|
|
opt_print_header='no'
|
|
;;
|
|
--print-raw )
|
|
opt_print_raw='yes'
|
|
opt_print_header='no'
|
|
;;
|
|
--release-stage | --release-stage=* )
|
|
if [[ "$1" == "--release" ]]; then
|
|
local arg=$2
|
|
shift
|
|
else
|
|
local arg=${1/--release=}
|
|
fi
|
|
is_release_stage "${arg}" || \
|
|
die_relstage_invalid "${arg}"
|
|
opt_use_relstages+="${arg}:"
|
|
;;
|
|
--with | --with=* )
|
|
if [[ "$1" == --with ]]; then
|
|
local arg=$2
|
|
shift
|
|
else
|
|
local arg=${1/--with=}
|
|
fi
|
|
if [[ -z ${arg} ]] || [[ "${arg}" == -* ]]; then
|
|
die_args_invalid_value '--with' "${arg}"
|
|
fi
|
|
arg=${arg//:/ }
|
|
arg=${arg//,/ }
|
|
for module in ${arg}; do
|
|
with_modules+=" && / ${module//\//\\/}/"
|
|
done
|
|
;;
|
|
-a | --all-releases-stages )
|
|
opt_use_relstages+="${ReleaseStages}"
|
|
;;
|
|
-v | --verbose )
|
|
opt_print_verbose='yes'
|
|
;;
|
|
--wrap )
|
|
opt_wrap='yes'
|
|
;;
|
|
--newest )
|
|
opt_newest='yes'
|
|
;;
|
|
-- )
|
|
shift 1
|
|
modules+=( "$@" )
|
|
break
|
|
;;
|
|
* )
|
|
modules+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
[[ "${opt_use_relstages}" == ":" ]] && opt_use_relstages=":${UsedReleaseStages}:"
|
|
[[ ${#modules[@]} == 0 ]] && modules+=( '' )
|
|
|
|
#......................................................................
|
|
# collect directories containing modulefiles -> modulepath
|
|
local -a groups=( "${!GroupDepths[@]}" )
|
|
local -a modulepath_spack=()
|
|
local -a modulepath_pmodules=()
|
|
local -a modulepath_other=()
|
|
# search in overlays with layout 'Spack'
|
|
for ol_name in "${UsedOverlays[@]}"; do
|
|
[[ "${OverlayInfo[${ol_name}:layout]}" != 'Spack' ]] && break
|
|
[[ "${OverlayInfo[${ol_name}:type]}" == "${ol_replacing}" ]] && \
|
|
groups=()
|
|
local -- path=''
|
|
if [[ ":${UsedReleaseStages}:" == *:unstable:* ]]; then
|
|
path="${OverlayInfo[${ol_name}:modulepath_unstable]}"
|
|
elif [[ ":${UsedReleaseStages}:" == *:deprecated:* ]]; then
|
|
path="${OverlayInfo[${ol_name}:modulepath_deprecated]}"
|
|
else
|
|
path="${OverlayInfo[${ol_name}:modulepath_stable]}"
|
|
fi
|
|
[[ -z "${path}" ]] && path="${OverlayInfo[${ol_name}:modulepath]}"
|
|
[[ -z "${path}" ]] && continue
|
|
|
|
IFS=':' read -r -a modulepath_spack <<<"${path}"
|
|
done
|
|
|
|
# search in overlays with layout 'Pmodules'
|
|
for group in "${groups[@]}"; do
|
|
for ol_name in "${UsedOverlays[@]}"; do
|
|
[[ "${OverlayInfo[$ol_name:layout]}" != 'Pmodules' ]] && continue
|
|
local -- dir="${OverlayInfo[${ol_name}:modulefiles_root]}"
|
|
dir+="/${group}/${__MODULEFILES_DIR__}"
|
|
[[ -r "${dir}" ]] && modulepath_pmodules+=( "${dir}" )
|
|
done
|
|
done
|
|
|
|
# add directories in modulpath outside overlays
|
|
local -a dirs
|
|
IFS=':' read -r -a dirs <<<"${MODULEPATH}"
|
|
for dir in "${dirs[@]}"; do
|
|
local -- ol=''
|
|
local -- grp=''
|
|
find_overlay ol grp "${dir}" && continue
|
|
[[ -r "${dir}" ]] && modulepath_other+=( "${dir}" )
|
|
done
|
|
|
|
local -a modulepath=()
|
|
printf -v modulepath "%s:" \
|
|
"${modulepath_spack[@]}" \
|
|
"${modulepath_pmodules[@]}" \
|
|
"${modulepath_other[@]}"
|
|
modulepath="${modulepath%:}"
|
|
|
|
local hash=$(echo "${modulepath}" | md5sum)
|
|
SpiderCache="${CacheDir}/spider-${hash:0:16}.yaml"
|
|
|
|
local -- update_cache='yes'
|
|
if [[ -r "${SpiderCache}" ]]; then
|
|
local -i mtime=0
|
|
mtime=$( ${stat} --format %Y "${SpiderCache}" )
|
|
# cache expiration time is 4h
|
|
(( CurTime - mtime <= 14400 )) && update_cache='no'
|
|
fi
|
|
if [[ "${update_cache}" == 'yes' ]]; then
|
|
std::info "(Re-)building the Pmodules cache. Please be patient ..."
|
|
spider_output="$( ${Spider_cmd} -o spider-json "${modulepath}" | \
|
|
${yq} -p j -o y '.*' |
|
|
${yq} '(.*.parentAA|select(.)) as $i ireduce({}; setpath($i | path; $i))')"
|
|
echo "${spider_output}" > "${SpiderCache}"
|
|
std::info "Done ..."
|
|
else
|
|
spider_output=$(< "${SpiderCache}")
|
|
fi
|
|
local -a keys=()
|
|
mapfile -t keys < <( ${yq} '.|keys[]' "${SpiderCache}" )
|
|
local -- key=''
|
|
for key in "${keys[@]}"; do
|
|
spider_keys[${key}]=1
|
|
done
|
|
|
|
#......................................................................
|
|
local -- module=''
|
|
for module in "${modules[@]}"; do
|
|
search "${module}" \
|
|
"${modulepath_spack[@]%/Core}" \
|
|
"${modulepath_pmodules[@]}" \
|
|
"${modulepath_other[@]}"
|
|
done
|
|
print_result
|
|
|
|
# empty tmp file
|
|
echo -n '' > ${TmpFile}
|
|
}
|
|
|
|
##############################################################################
|
|
find_matching_modules(){
|
|
local -r __doc__='
|
|
Find modules matching the specified arguments.
|
|
Return "table" with the columns:
|
|
name-name release-stage group modulefile overlay deps
|
|
'
|
|
local -n result="$1"
|
|
shift
|
|
local -a opts=('--print-raw')
|
|
local -a patterns=()
|
|
while (( $# > 0 )); do
|
|
case $1 in
|
|
-* )
|
|
opts+=( "$1" )
|
|
;;
|
|
* )
|
|
patterns+=( "$1")
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
result=$(set +x; subcommand_search "${patterns[@]}" "${opts[@]}")
|
|
}
|
|
|
|
##############################################################################
|
|
create_module_tab(){
|
|
local -n result="$1"
|
|
local -- modules="$2"
|
|
|
|
local -- modulename=''
|
|
local -- relstage=''
|
|
local -- grp=''
|
|
local -- modulefile=''
|
|
local -- ol_name=''
|
|
local -- deps=''
|
|
|
|
local -- rest=''
|
|
local -i size_of_first_col=0
|
|
local -i num_rows=0
|
|
while read modulename rest; do
|
|
[[ -z "${modulename}" ]] && continue
|
|
(( ${#modulename} > size_of_first_col )) && size_of_first_col=${#modulename}
|
|
(( num_rows++ ))
|
|
done <<<"${modules}"
|
|
result=''
|
|
(( num_rows == 0 )) && return 1
|
|
printf -v result "%-${size_of_first_col}s %-10s %s\n" \
|
|
"Module" "Group" "Dependencies"
|
|
|
|
while read modulename relstage grp modulefile ol_name deps; do
|
|
[[ -z "${modulename}" ]] && continue
|
|
local -- str=''
|
|
printf -v str "%-${size_of_first_col}s " "${modulename}"
|
|
result+="${str}"
|
|
if [[ "${grp}" == 'none' ]]; then
|
|
grp='-'
|
|
elif [[ ":${UsedGroups}:" != *:${grp}:* ]]; then
|
|
grp="(${grp})"
|
|
fi
|
|
printf -v str "%-10s " "${grp}"
|
|
result+="${str}"
|
|
result+="${deps}"
|
|
result+='\n'
|
|
done <<<"${modules}"
|
|
return 0
|
|
}
|
|
|
|
##############################################################################
|
|
Subcommands['help']='help'
|
|
Options['help']='-o hHV\? -l version -l help'
|
|
Help['help']='
|
|
USAGE:
|
|
module [ switches ] [ subcommand ] [subcommand-args ]
|
|
|
|
SWITCHES:
|
|
-h|-H|-?|--help this usage info
|
|
-V|--version modules version & configuration options
|
|
--debug enable debug output
|
|
|
|
SUBCOMMANDS:
|
|
+ add|load [switches] modulefile [modulefile ...]
|
|
+ rm|unload modulefile [modulefile ...]
|
|
+ switch|swap [modulefile1] modulefile2
|
|
+ display|show modulefile [modulefile ...]
|
|
+ avail [switches] [modulefile [modulefile ...]]
|
|
+ search [switches] [args]
|
|
+ use [switches] [dir|group|release ...]
|
|
+ unuse dir|group|release [dir|group|release ...]
|
|
+ refresh
|
|
+ purge
|
|
+ list [switches]
|
|
+ clear
|
|
+ help [modulefile|subcommand]
|
|
+ whatis [modulefile [modulefile ...]]
|
|
+ apropos|keyword string
|
|
+ save collection
|
|
+ restore collection
|
|
+ savelist
|
|
+ saverm collection
|
|
+ saveshow collection
|
|
+ initadd modulefile [modulefile ...]
|
|
+ initprepend modulefile [modulefile ...]
|
|
+ initrm modulefile [modulefile ...]
|
|
+ initswitch modulefile1 modulefile2
|
|
+ initlist
|
|
+ initclear
|
|
|
|
DOCUMENTATION:
|
|
Full documentation is available at
|
|
http://pmodules.gitpages.psi.ch
|
|
'
|
|
#..............................................................................
|
|
subcommand_help() {
|
|
print_help_hints(){
|
|
local -- pattern="$1"
|
|
local -- modules=''
|
|
find_matching_modules modules "${pattern}" -a
|
|
|
|
std::info "A module with the name '${pattern}' is not available in the current module path!\n"
|
|
local -- output=''
|
|
create_module_tab output "${modules}" || return 0
|
|
std::info "The following modules are matching the given name:\n"
|
|
std::info "${output}"
|
|
std::info "To get help for a specific module, the runtime dependencies must be loaded first!"
|
|
}
|
|
|
|
local -r __doc__='Implementation of the sub-command help.'
|
|
local -a args=()
|
|
while (( $# > 0 )); do
|
|
case $1 in
|
|
-\? | -h | -H | --help )
|
|
print_help "${SubCommand}"
|
|
;;
|
|
-V | --version )
|
|
print_help 'version'
|
|
;;
|
|
-- )
|
|
shift 1
|
|
args+=( "$@" )
|
|
break
|
|
;;
|
|
* )
|
|
args+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
if (( ${#args[@]} == 0 )); then
|
|
print_help 'help'
|
|
fi
|
|
local arg
|
|
for arg in "${args[@]}"; do
|
|
if [[ -v Help[${arg}] ]] ; then
|
|
print_help "${arg}"
|
|
continue
|
|
fi
|
|
local -- modulefile=''
|
|
local -- relstage=''
|
|
local -- moduledir=''
|
|
if ! find_modulefile \
|
|
modulefile \
|
|
relstage \
|
|
moduledir \
|
|
modulecmd \
|
|
"${arg}"; then
|
|
print_help_hints "${arg}"
|
|
continue
|
|
fi
|
|
"${modulecmd}" 'bash' 'help' "${modulefile}"
|
|
done
|
|
}
|
|
|
|
##############################################################################
|
|
Subcommands['whatis']='whatis'
|
|
Options['whatis']='-o \?Ha -l help -l all'
|
|
Help['whatis']='
|
|
USAGE:
|
|
module whatis [modulefile...]
|
|
Display the information set up by the module-whatis commands
|
|
inside the specified modulefile(s). If no modulefile is
|
|
specified, all whatis lines will be shown.
|
|
'
|
|
#..............................................................................
|
|
subcommand_whatis() {
|
|
local -r __doc__='Implementation of the sub-command whatis.'
|
|
local -- opts=('--newest')
|
|
local -- args=()
|
|
|
|
while (( $# > 0 )); do
|
|
case $1 in
|
|
-\? | --help )
|
|
print_help "${SubCommand}"
|
|
;;
|
|
-a | --all )
|
|
opts+=( '-a' )
|
|
;;
|
|
-- )
|
|
shift 1
|
|
args+=( "$@" )
|
|
break
|
|
;;
|
|
* )
|
|
args+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
local -- modules=''
|
|
find_matching_modules modules \
|
|
"${opts[@]}" \
|
|
"${args[@]}"
|
|
|
|
local -- modulename=''
|
|
local -- relstage=''
|
|
local -- grp=''
|
|
local -- modulefile=''
|
|
local -- ol_name=''
|
|
local -- deps=''
|
|
while read modulename relstage grp modulefile ol_name deps; do
|
|
[[ -n ${modulefile} ]] || continue
|
|
if [[ "${modulefile##*.}" == 'lua' ]]; then
|
|
modulecmd="${Lmod_cmd}"
|
|
modulefile="${modulename}"
|
|
else
|
|
modulecmd="${Tcl_cmd}"
|
|
fi
|
|
local whatis=''
|
|
whatis=$("${modulecmd}" bash \
|
|
whatis \
|
|
"${modulename}" \
|
|
2>&1 1>/dev/null)
|
|
[[ "${whatis}" == *:ERROR:* ]] && continue
|
|
whatis="${whatis/*:}" # remove modulefile name
|
|
printf "%-25s: %s\n" "${modulename}" "${whatis}" 1>&2
|
|
done <<<"${modules}"
|
|
}
|
|
|
|
##############################################################################
|
|
Subcommands['apropos']='apropos'
|
|
Subcommands['keyword']='apropos'
|
|
Options['apropos']='-o \?Ha -l help -l all'
|
|
Help['apropos']='
|
|
USAGE:
|
|
module apropos string
|
|
module keyword string Seeks through the whatis informations of
|
|
all modulefiles for the specified string. All module-whatis
|
|
informations matching the string will be displayed.
|
|
'
|
|
#..............................................................................
|
|
subcommand_apropos() {
|
|
local -r __doc__='Implementation of the sub-command apropos|keyword'
|
|
local opts=()
|
|
local args=()
|
|
while (( $# > 0 )); do
|
|
case $1 in
|
|
-\? | --help )
|
|
print_help "${SubCommand}"
|
|
;;
|
|
-a | --all )
|
|
opts+=( '-a' )
|
|
;;
|
|
-- )
|
|
shift 1
|
|
args+=( "$@" )
|
|
break
|
|
;;
|
|
* )
|
|
args+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
(( ${#args[@]} == 0 )) && \
|
|
die_args_missing
|
|
(( ${#args[@]} > 1 )) && \
|
|
die_args_too_many
|
|
|
|
local arg="${args[0],,}"
|
|
|
|
local -- modules=''
|
|
find_matching_modules modules '' "${opts[@]}"
|
|
|
|
local -- modulename=''
|
|
local -- relstage=''
|
|
local -- grp=''
|
|
local -- modulefiles=''
|
|
local -- ol_name=''
|
|
local -- deps=''
|
|
while read modulename relstage grp modulefile ol_name deps; do
|
|
[[ -z "${modulename}" ]] && continue
|
|
if [[ "${modulename##*.}" == 'lua' ]]; then
|
|
modulecmd="${Lmod_cmd}"
|
|
modulefile="${modulename}"
|
|
else
|
|
modulecmd="${Tcl_cmd}"
|
|
fi
|
|
local whatis=''
|
|
whatis=$("${modulecmd}" bash \
|
|
whatis \
|
|
"${modulefile}" \
|
|
2>&1 1>/dev/null)
|
|
[[ "${whatis}" == *:ERROR:* ]] && continue
|
|
whatis="${whatis/*:}"
|
|
[[ ${whatis,,} == *${arg}* ]] && \
|
|
printf "%-25s: %s\n" "${modulename}" "${whatis}" 1>&2
|
|
done <<<"${modules}"
|
|
}
|
|
##############################################################################
|
|
#
|
|
# Collections
|
|
#
|
|
# Collections describe a sequence of module use then module load commands
|
|
# that are interpreted by Modules to set the user environment as described
|
|
# by this sequence.
|
|
#
|
|
# User collections:
|
|
#
|
|
# System collections:
|
|
##
|
|
|
|
##############################################################################
|
|
Subcommands['save']='save'
|
|
Options['save']='-o \?H -l help'
|
|
Help['save']="
|
|
USAGE:
|
|
module save [collection]
|
|
Record the currently set MODULEPATH directory list and the
|
|
currently loaded modulefiles in a collection file under the
|
|
user's collection directory \$HOME/.Pmodules. If collection
|
|
name is not specified, then it is assumed to be the default
|
|
collection. If collection is a fully qualified path, it is
|
|
saved at this location rather than under the user's collection
|
|
directory.
|
|
"
|
|
#..............................................................................
|
|
declare -r UsrCollectionsDir="${HOME}/.Pmodules/collections"
|
|
subcommand_save(){
|
|
local -r __doc__='
|
|
Implementation of the save collection sub-command save.'
|
|
local -a args=()
|
|
local -- opt_system='no'
|
|
while (( $# > 0 )); do
|
|
case $1 in
|
|
-\? | -H | --help )
|
|
print_help "${SubCommand}"
|
|
;;
|
|
--system )
|
|
opt_system='yes'
|
|
;;
|
|
-- )
|
|
shift 1
|
|
args+=( "$@" )
|
|
break
|
|
;;
|
|
* )
|
|
args+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
(( ${#args[@]} > 1 )) && die_args_too_many
|
|
|
|
[[ ${args[0]:0:1} =~ [0-9a-zA-Z] ]] || \
|
|
die_col_invalid_name "${args[0]}"
|
|
|
|
local -- basedir="${UsrCollectionsDir}"
|
|
if [[ "${opt_system}" == 'yes' ]]; then
|
|
basedir="${UsedOverlays[0]}/collections"
|
|
fi
|
|
local collection=''
|
|
if (( ${#args[@]} == 0 )); then
|
|
collection="${basedir}/default"
|
|
else
|
|
collection="${basedir}/${args[0]}"
|
|
fi
|
|
|
|
mkdir -p "${collection%/*}" || \
|
|
die_cannot create_directory "$_"
|
|
|
|
# save used release stages, groups and overlays
|
|
local -- item=''
|
|
local -a items=()
|
|
local -a tmp=()
|
|
local -- grp=''
|
|
|
|
IFS=':' read -r -a tmp <<< "${UsedReleaseStages}"
|
|
items+=( "${tmp[@]}")
|
|
IFS=':' read -r -a tmp <<< "${UsedGroups}"
|
|
for grp in "${tmp[@]}"; do
|
|
# skip hierarchical groups
|
|
(( ${GroupDepths[${tmp}]} > 0 )) && continue
|
|
items+=( "${grp}")
|
|
done
|
|
IFS=':' read -r -a tmp <<< "${UsedOverlays}"
|
|
items+=( "${tmp[@]}")
|
|
for item in "${items[@]}"; do
|
|
[[ "${item}" == 'base' ]] && continue
|
|
s+="module use ${item};\n"
|
|
done
|
|
|
|
# save additional module directories
|
|
local -a modulepath=()
|
|
IFS=':' read -r -a modulepath <<< "${MODULEPATH}"
|
|
local dir=''
|
|
local ol=''
|
|
local grp=''
|
|
for dir in "${modulepath[@]}"; do
|
|
find_overlay ol grp "${dir}" && continue
|
|
s+="module use \"${dir}\";\n"
|
|
done
|
|
|
|
# save loaded modules
|
|
local -a modules=()
|
|
IFS=':' read -r -a modules <<< "${LOADEDMODULES}"
|
|
local -- m=''
|
|
for m in "${modules[@]}"; do
|
|
[[ $m == Pmodules/* ]] && continue
|
|
s+="module load $m;\n"
|
|
done
|
|
|
|
# save collection
|
|
echo -e "$s" > "${collection}" || \
|
|
die_col_cannot_be_saved "${collection}"
|
|
}
|
|
|
|
##############################################################################
|
|
Subcommands['restore']='restore'
|
|
Options['restore']='-o \?H -l help'
|
|
Help['restore']="
|
|
USAGE:
|
|
module restore [collection]
|
|
Restore the environment state as defined in collection.
|
|
If collection name is not specified, then it is assumed
|
|
to be the default collection.
|
|
|
|
"
|
|
#..............................................................................
|
|
search_collection(){
|
|
local -n _path="$1"
|
|
local -- _collection="$2"
|
|
|
|
if [[ -r "${UsrCollectionsDir}/${_collection}" ]]; then
|
|
_path="${UsrCollectionsDir}"
|
|
return 0
|
|
fi
|
|
local -- _ol
|
|
for _ol in "${UsedOverlays[@]}"; do
|
|
if [[ -r "${OverlayInfo[${_ol}:install_root]}/collections/${_collection}" ]]; then
|
|
_path="${OverlayInfo[${_ol}:install_root]}/collections"
|
|
return 0
|
|
fi
|
|
done
|
|
die_col_doesnt_exist "${_collection}"
|
|
}
|
|
|
|
#..............................................................................
|
|
subcommand_restore() {
|
|
local -r __doc__='
|
|
Implementation of the restore collection sub-command.'
|
|
local -a args=()
|
|
while (( $# > 0 )); do
|
|
case $1 in
|
|
-\? | -H | --help )
|
|
print_help "${SubCommand}"
|
|
;;
|
|
-- )
|
|
shift 1
|
|
args+=( "$@" )
|
|
break
|
|
;;
|
|
* )
|
|
args+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
(( ${#args[@]} > 1 )) && die_args_too_many
|
|
|
|
(( ${#args[@]} == 0 )) && args=( 'default' )
|
|
local -- path=''
|
|
local -- collection=''
|
|
local -a collections=()
|
|
for collection in "${args[@]}"; do
|
|
search_collection path "${collection}"
|
|
collections+=( "${path}/${collection}" )
|
|
done
|
|
|
|
# reset/unload everything (except Pmodules/<version>)
|
|
IFS=':' read -r -a modules <<< "${LOADEDMODULES}"
|
|
local -- m=''
|
|
for ((i=${#modules[@]}-1; i>=0; i--)); do
|
|
[[ ${modules[$i]} == Pmodules/* ]] && continue
|
|
subcommand_unload "${modules[$i]}"
|
|
done
|
|
|
|
local -a items=()
|
|
local -a tmp=()
|
|
local -- item=''
|
|
local -a default_grps=()
|
|
|
|
IFS=':' read -r -a tmp <<< "${UsedReleaseStages}"
|
|
items+=( "${tmp[@]}")
|
|
|
|
# remove all groups with the exception of the default groups
|
|
IFS=':' read -r -a tmp <<< "${UsedGroups}"
|
|
IFS=':' read -r -a default_grps <<< "${DefaultGroups}"
|
|
for item in "${tmp[@]}"; do
|
|
(( ${GroupDepths["${item}"]} > 0 )) && continue
|
|
std::is_member_of_array "${item}" default_grps && continue
|
|
items+=( "${item}" )
|
|
done
|
|
IFS=':' read -r -a tmp <<< "${UsedOverlays}"
|
|
for item in "${tmp[@]}"; do
|
|
[[ "${item}" == 'base' ]] && continue
|
|
items+=( "${tmp[@]}")
|
|
done
|
|
|
|
local -- item=''
|
|
for item in "${items[@]}"; do
|
|
[[ -z "${item}" ]] && continue
|
|
subcommand_unuse "${item}"
|
|
done
|
|
|
|
# load collection
|
|
for collection in "${collections[@]}"; do
|
|
${cat} "${collection}"
|
|
done
|
|
vars_to_be_exported['PMODULES_ENV']=1
|
|
}
|
|
|
|
##############################################################################
|
|
Subcommands['savelist']='savelist'
|
|
Options['savelist']='-o \?H -l help'
|
|
Help['savelist']="
|
|
USAGE:
|
|
module savelist [pattern...]
|
|
List collections that are currently saved under the user's
|
|
collection directory.
|
|
If a pattern is given, then the collections are filtered to
|
|
only list those whose name matches this pattern. It may
|
|
contain wildcard characters. pattern is matched in a case
|
|
insensitive manner by default. If multiple patterns are
|
|
given, collection has to match at least one of them to be
|
|
listed.
|
|
"
|
|
#..............................................................................
|
|
subcommand_savelist() {
|
|
local -r __doc__='
|
|
Implementation of the savelist collection sub-command.'
|
|
get_collections() {
|
|
local -n _result="$1"
|
|
local -n gc_dirs="$2"
|
|
shift 2
|
|
_result=()
|
|
local _pattern
|
|
local _coll
|
|
for _pattern in "$@"; do
|
|
while read -r _coll; do
|
|
_result+=( "${_coll}" )
|
|
done < <(${find} "${gc_dirs[@]}" \
|
|
-type f \
|
|
-ipath "*/${_pattern}" \
|
|
-printf "%P\n" 2>/dev/null)
|
|
done
|
|
}
|
|
|
|
print_collections() {
|
|
local -r prt_header="$1"
|
|
local -n prt_dirs="$2"
|
|
shift 2
|
|
local -a prt_collections=()
|
|
get_collections prt_collections prt_dirs "$@"
|
|
|
|
(( ${#prt_collections[@]} == 0 )) && return 0
|
|
|
|
std::info "${prt_header}"
|
|
for prt_col in "${prt_collections[@]}"; do
|
|
std::info "\t${prt_col}"
|
|
done
|
|
}
|
|
|
|
local -a args=()
|
|
while (( $# > 0 )); do
|
|
case $1 in
|
|
-\? | -H | --help )
|
|
print_help "${SubCommand}"
|
|
;;
|
|
-- )
|
|
shift 1
|
|
args+=( "$@" )
|
|
break
|
|
;;
|
|
* )
|
|
args+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
if (( ${#args[@]} == 0 )); then
|
|
args[0]='*'
|
|
fi
|
|
local -a _dirs=( "${UsrCollectionsDir[@]}" )
|
|
print_collections "User collections:" _dirs "${args[@]}"
|
|
_dirs=()
|
|
local _ol
|
|
for _ol in "${UsedOverlays[@]}"; do
|
|
_dirs+=( "${OverlayInfo[${_ol}:install_root]}/collections" )
|
|
done
|
|
print_collections "\nSystem collections:" _dirs "${args[@]}"
|
|
}
|
|
|
|
##############################################################################
|
|
Subcommands['saverm']='saverm'
|
|
Options['saverm']='-o \?H -l help'
|
|
Help['saverm']="
|
|
USAGE:
|
|
module saverm [collection]
|
|
Delete the collection file under the user's collection
|
|
directory. If collection name is not specified, then it
|
|
is assumed to be the default collection.
|
|
"
|
|
#..............................................................................
|
|
subcommand_saverm() {
|
|
local -r __doc__='
|
|
Implementation of the saverm collection sub-command.'
|
|
|
|
local -a args=()
|
|
while (( $# > 0 )); do
|
|
case $1 in
|
|
-\? | -H | --help )
|
|
print_help "${SubCommand}"
|
|
;;
|
|
-- )
|
|
shift 1
|
|
args+=( "$@" )
|
|
break
|
|
;;
|
|
* )
|
|
args+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
if (( ${#args[@]} == 0 )); then
|
|
args[0]='default'
|
|
fi
|
|
local -- path=''
|
|
local -- collection=''
|
|
for collection in "${args[@]}"; do
|
|
search_collection path "${collection}"
|
|
test -e "${path}/${collection}" || \
|
|
die_col_doesnt_exist "${collection}"
|
|
${rm} -f "${path}/${collection}" 2>/dev/null || \
|
|
die_col_cannot_be_removed "${collection}"
|
|
# remove directories if empty
|
|
${rmdir} -p "${path}/${collection%%/*}" 2>/dev/null
|
|
done
|
|
}
|
|
|
|
##############################################################################
|
|
Subcommands['saveshow']='saveshow'
|
|
Options['saveshow']='-o \?H -l help'
|
|
Help['saveshow']="
|
|
USAGE:
|
|
module saveshow [collection...]
|
|
Display the content of collection. If collection name is not
|
|
specified, then it is assumed to be the default collection,
|
|
"
|
|
#..............................................................................
|
|
subcommand_saveshow() {
|
|
local -r __doc__='
|
|
Implementation of the saveshow collection sub-command.'
|
|
|
|
local -a args=()
|
|
while (( $# > 0 )); do
|
|
case $1 in
|
|
-\? | -H | --help )
|
|
print_help "${SubCommand}"
|
|
;;
|
|
-- )
|
|
shift 1
|
|
args+=( "$@" )
|
|
break
|
|
;;
|
|
* )
|
|
args+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
if (( ${#args[@]} == 0 )); then
|
|
args[0]='default'
|
|
fi
|
|
local -- collection=''
|
|
local -- path=''
|
|
for collection in "${args[@]}"; do
|
|
search_collection path "${collection}"
|
|
test -e "${path}/${collection}" || \
|
|
die_col_doesnt_exist "${collection}"
|
|
std::info "Collection '${collection}':"
|
|
cat "${path}/${collection}" 1>&2
|
|
done
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# sub-commands to manipulate the shell profile.
|
|
#
|
|
##
|
|
|
|
##############################################################################
|
|
Subcommands['initadd']='initadd'
|
|
Options['initadd']='-o \?H -l help'
|
|
Help['initadd']="
|
|
USAGE:
|
|
module initadd modulefile...
|
|
Add modulefile(s) to the shell's initialization file in the
|
|
user's home directory. The startup files checked (in order)
|
|
are:
|
|
|
|
csh - .modules, .cshrc(.ext), .csh_variables, and
|
|
.login(.ext)
|
|
tcsh - .modules, .tcshrc, .cshrc(.ext), .csh_variables,
|
|
and .login(.ext)
|
|
(k)sh - .modules, .profile(.ext), and .kshenv(.ext)
|
|
bash - .modules, .bash_profile, .bash_login,
|
|
.profile(.ext) and .bashrc(.ext)
|
|
zsh - .modules, .zcshrc(.ext), .zshenv(.ext), and
|
|
.zlogin(.ext)
|
|
|
|
If a 'module load' line is found in any of these files, the
|
|
modulefile(s) is(are) appended to any existing list of
|
|
modulefiles. The 'module load' line must be located in at
|
|
least one of the files listed above for any of the 'init'
|
|
sub-commands to work properly. If the 'module load' line
|
|
line is found in multiple shell initialization files, all
|
|
of the lines are changed.
|
|
"
|
|
#..............................................................................
|
|
subcommand_initadd() {
|
|
local -r __doc__='
|
|
Implementation of the initadd sub-command.'
|
|
|
|
subcommand_generic1plus "$@"
|
|
}
|
|
|
|
##############################################################################
|
|
Subcommands['initprepend']='initprepend'
|
|
Options['initprepend']='-o \?H -l help'
|
|
Help['initprepend']="
|
|
USAGE:
|
|
module initprepend modulefile...
|
|
Does the same as initadd but prepends the given modules to
|
|
the beginning of the list.
|
|
"
|
|
#..............................................................................
|
|
subcommand_initprepend() {
|
|
local -r __doc__='
|
|
Implementation of the initprepend sub-command.'
|
|
|
|
subcommand_generic1plus "$@"
|
|
}
|
|
|
|
##############################################################################
|
|
Subcommands['initrm']='initrm'
|
|
Options['initrm']='-o \?H -l help'
|
|
Help['initrm']="
|
|
USAGE:
|
|
module initrm modulefile...
|
|
Remove modulefile(s) from the shell's initialization files.
|
|
"
|
|
#..............................................................................
|
|
subcommand_initrm() {
|
|
local -r __doc__='
|
|
Implementation of the initrm sub-command.'
|
|
|
|
subcommand_generic1plus "$@"
|
|
}
|
|
|
|
##############################################################################
|
|
Subcommands['initswitch']='initswitch'
|
|
Options['initswitch']='-o \?H -l help'
|
|
Help['initswitch']="
|
|
USAGE:
|
|
module initswitch modulefile1 modulefile2
|
|
Switch modulefile1 with modulefile2 in the shell's
|
|
initialization files.
|
|
"
|
|
#..............................................................................
|
|
subcommand_initswitch() {
|
|
local -r __doc__='
|
|
Implementation of the initswitch sub-command.'
|
|
|
|
local args=()
|
|
while (( $# > 0 )); do
|
|
case $1 in
|
|
-\? | --help )
|
|
print_help "${SubCommand}"
|
|
;;
|
|
-- )
|
|
shift 1
|
|
args+=( "$@" )
|
|
break
|
|
;;
|
|
* )
|
|
args+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
(( ${#args[@]} != 2 )) && \
|
|
die_args_wrong_number
|
|
"${modulecmd}" "${Shell}" "${SubCommand}" "${args[@]}"
|
|
}
|
|
|
|
##############################################################################
|
|
Subcommands['initlist']='initlist'
|
|
Options['initlist']='-o \?H -l help'
|
|
Help['initlist']="
|
|
USAGE:
|
|
module initlist
|
|
List all of the modulefiles loaded from the shell's
|
|
initialization file.
|
|
"
|
|
#..............................................................................
|
|
subcommand_initlist() {
|
|
local -r __doc__='
|
|
Implementation of the initlist sub-command.'
|
|
|
|
subcommand_generic0 "$@"
|
|
}
|
|
|
|
##############################################################################
|
|
Subcommands['initclear']='initclear'
|
|
Options['initclear']='-o \?H -l help'
|
|
Help['initclear']="
|
|
USAGE:
|
|
module initclear
|
|
Clear all of the modulefiles from the shell's
|
|
initialization files.
|
|
"
|
|
#..............................................................................
|
|
subcommand_initclear() {
|
|
local -r __doc__='
|
|
Implementation of the initclear sub-command.'
|
|
|
|
subcommand_generic0 "$@"
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# main
|
|
#
|
|
(( $# > 0 )) || print_help 'help'
|
|
|
|
# the debug() function will be re-defined if modulecmd is called with the
|
|
# option '--verbose'. See below.
|
|
debug(){
|
|
:
|
|
}
|
|
|
|
# first argument must be a shell!
|
|
case "$1" in
|
|
sh | bash | zsh )
|
|
declare Shell="sh"
|
|
;;
|
|
csh | tcsh )
|
|
declare Shell='csh'
|
|
;;
|
|
python )
|
|
declare Shell='python'
|
|
;;
|
|
* )
|
|
die_args_unsupported_shell "$1"
|
|
;;
|
|
esac
|
|
shift
|
|
|
|
# Parse agruments till and including the sub-command.
|
|
# The arguments for the sub-command are parsed later.
|
|
declare -a opts=()
|
|
while (( $# > 0 )); do
|
|
case $1 in
|
|
-\? | -H | --help | -help )
|
|
print_help 'help'
|
|
;;
|
|
-V | --version )
|
|
print_help 'version'
|
|
;;
|
|
--debug )
|
|
set -x
|
|
;;
|
|
--verbose )
|
|
debug(){
|
|
echo "INFO: " "$@" 1>&2
|
|
}
|
|
;;
|
|
'' )
|
|
;;
|
|
-* )
|
|
opts+=( "$1" )
|
|
;;
|
|
* )
|
|
SubCommand="$1"
|
|
shift
|
|
break
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
[[ -z "${SubCommand}" ]] && die_args_subcmd_missing
|
|
[[ -v Subcommands[${SubCommand}] ]] || die_args_invalid_subcmd "${SubCommand}"
|
|
|
|
# We (re-)initialise the Pmodules system, if
|
|
# PMODULES_ENV or MODULEPATH was not set/is empty
|
|
if [[ -z ${PMODULES_ENV} ]] || [[ -z "${MODULEPATH}" ]]; then
|
|
pmodules_setup 'init'
|
|
else
|
|
# restore variables from last call
|
|
eval "$("${base64}" -d <<< "${PMODULES_ENV}" 2>/dev/null)"
|
|
fi
|
|
|
|
# re-read config if Pmodules has been changed
|
|
if [[ "${Version}" != "${PMODULES_VERSION}" ]]; then
|
|
pm::read_config
|
|
for ol_name in "${UsedOverlays[@]}"; do
|
|
OverlayInfo[${ol_name}:used]='yes'
|
|
done
|
|
Version="${PMODULES_VERSION}"
|
|
vars_to_be_exported['PMODULES_ENV']=1
|
|
fi
|
|
|
|
# make sure that these variables are set
|
|
if [[ -z "${UsedGroups}" ]] || \
|
|
[[ -z "${UsedReleaseStages}" ]] || \
|
|
(( ${#UsedOverlays[@]} == 0 )); then
|
|
pmodules_setup
|
|
fi
|
|
|
|
# repace alias with the real "name"
|
|
SubCommand=${Subcommands[${SubCommand}]}
|
|
|
|
# parse arguments of the sub-command and call it
|
|
temp=$("${getopt}" --name="${CMD}" ${Options[${SubCommand}]} -- "${opts[@]}" "$@" ) \
|
|
|| print_help "${SubCommand}"
|
|
eval set -- "${temp}"
|
|
unset temp
|
|
|
|
"subcommand_${SubCommand}" "$@"
|
|
|
|
# Local Variables:
|
|
# mode: sh
|
|
# sh-basic-offset: 8
|
|
# tab-width: 8
|
|
# End:
|