mirror of
https://github.com/Pmodules/Pmodules.git
synced 2026-06-23 16:17:59 +02:00
3291 lines
86 KiB
Plaintext
3291 lines
86 KiB
Plaintext
#!@BASH@ --noprofile
|
|
#
|
|
|
|
#
|
|
# which information do we need for overlays?
|
|
# - list of used overlays by name
|
|
# - dict to map an overlay name to install_root
|
|
# - dict to map an overlay name to modulefile_root
|
|
# - dict to map an overlay name to the overlay type
|
|
# - dict to map moduledirs to overlay name
|
|
|
|
unset CDPATH # unset CDPATH, otherwise 'cd' prints the directoy!
|
|
unset IFS # use default IFS
|
|
|
|
shopt -s nullglob
|
|
|
|
# used in some output messages only
|
|
declare -r CMD='module'
|
|
declare subcommand=''
|
|
|
|
declare -r mydir=$(cd $(dirname "$0") && pwd)
|
|
declare -- prefix=$(dirname "${mydir}")
|
|
declare -r libdir="${prefix}/lib"
|
|
declare -r libexecdir="${prefix}/libexec"
|
|
|
|
source "${libdir}/libstd.bash"
|
|
source "${libdir}/libpmodules.bash"
|
|
|
|
declare -r os_name="$(uname -s)"
|
|
|
|
path="${libexecdir}:/bin:/usr/bin"
|
|
std::def_cmds "${path}" \
|
|
'awk' 'base64' 'find' 'getopt' 'logger' 'mktemp' \
|
|
'rm' 'sed' 'sort' 'yq'
|
|
|
|
declare -rx TCL_LIBRARY="${PMODULES_HOME}/lib/tcl@TCL_VERSION@"
|
|
declare -x TCLLIBPATH
|
|
std::prepend_path TCLLIBPATH "${PMODULES_HOME}/lib/Pmodules"
|
|
|
|
declare -r tcl_cmd="${libexecdir}/modulecmd.bin"
|
|
declare -r lmod_cmd="${PMODULES_HOME}/libexec/lmod/lmod/libexec/lmod"
|
|
declare -- modulecmd="${tcl_cmd}"
|
|
|
|
declare -- verbosity_lvl=${PMODULES_VERBOSITY:-'verbose'}
|
|
|
|
declare -- Shell=''
|
|
|
|
# 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'
|
|
|
|
# not used here but in modbuild
|
|
declare -- TmpDir="/opt/psi/var/tmp/${USER}"
|
|
declare -- DistfilesDir="/opt/psi/var/distfiles"
|
|
|
|
|
|
export_env() {
|
|
case "${Shell}" in
|
|
sh | bash | zsh )
|
|
local -r fmt="export %s=\"%s\"; "
|
|
;;
|
|
csh | tcsh )
|
|
local -r fmt="setenv %s \"%s\"; "
|
|
;;
|
|
python )
|
|
local -r fmt="os.environ['%s'] = '%s'\n"
|
|
;;
|
|
* )
|
|
std::die 1 "Unsupported shell -- ${Shell}"
|
|
;;
|
|
esac
|
|
|
|
while (( $# > 0 )); do
|
|
printf "${fmt}" "$1" "${!1}"
|
|
shift
|
|
done
|
|
# :FIXME: UsedGroups can be modified in libmodule.tcl using
|
|
# append-path/remove-path! But we keep the state in PMODULES_ENV
|
|
# so we don't have to export it.
|
|
#
|
|
#case "${Shell}" in
|
|
#sh | bash | zsh )
|
|
# echo "unset UsedGroups; "
|
|
# ;;
|
|
#csh | tcsh )
|
|
# echo "unsetenv UsedGroups; "
|
|
# ;;
|
|
#esac
|
|
}
|
|
|
|
#
|
|
# Save/cache state in the environment variable PMODULES_ENV. The content is
|
|
# base64 encoded. This function is called on exit via a trap handler.
|
|
#
|
|
# Arguments:
|
|
# none
|
|
#
|
|
|
|
declare g_env_must_be_saved='no'
|
|
|
|
save_env() {
|
|
encode_base64(){
|
|
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
|
|
}
|
|
|
|
[[ $1 == 'no' ]] && return 0
|
|
local vars=( Version )
|
|
vars+=( UsedReleaseStages UsedGroups )
|
|
vars+=( DefaultGroups DefaultReleaseStages )
|
|
vars+=( ReleaseStages )
|
|
vars+=( GroupDepths )
|
|
vars+=( Overlays )
|
|
vars+=( UsedOverlays )
|
|
vars+=( OverlayInfo Dir2OverlayMap)
|
|
|
|
local s=$(typeset -p ${vars[@]})
|
|
declare -gx PMODULES_ENV=$( encode_base64 "$s" )
|
|
export_env 'PMODULES_ENV'
|
|
}
|
|
|
|
#
|
|
# function called on exit
|
|
#
|
|
_exit() {
|
|
save_env "${g_env_must_be_saved}"
|
|
if [[ -n "${tmpfile}" ]] && [[ -e "${tmpfile}" ]]; then
|
|
${rm} -f "${tmpfile}" || :
|
|
fi
|
|
}
|
|
trap '_exit' EXIT
|
|
|
|
die_missing_arg(){
|
|
std::die 3 "%s %s: %s\n" \
|
|
"${CMD}" "${subcommand}" 'missing argument'
|
|
}
|
|
|
|
die_too_many_args(){
|
|
std::die 3 "%s %s: %s\n" \
|
|
"${CMD}" "${subcommand}" 'too many arguments'
|
|
}
|
|
|
|
die_no_args_allowed(){
|
|
std::die 3 "%s %s: %s\n" \
|
|
"${CMD}" "${subcommand}" "no arguments allowed"
|
|
}
|
|
|
|
die_wrong_number_of_args(){
|
|
std::die 1 "%s %s: %s\n" \
|
|
"${CMD}" "${subcommand}" "wrong number of arguments"
|
|
}
|
|
|
|
die_illegal_opt(){
|
|
std::die 3 "%s %s: %s -- %s\n" \
|
|
"${CMD}" "${subcommand}" "illegal option" "$1"
|
|
}
|
|
|
|
die_illegal_arg(){
|
|
std::die 3 "%s %s: %s -- %s\n" \
|
|
"${CMD}" "${subcommand}" "invalid argument" "$1"
|
|
}
|
|
|
|
die_illegal_group(){
|
|
std::die 3 "%s %s: %s -- %s\n" \
|
|
"${CMD}" "${subcommand}" "invalid group name" "$1"
|
|
}
|
|
|
|
die_illegal_rel_stage(){
|
|
std::die 3 "%s %s: %s -- %s\n" \
|
|
"${CMD}" "${subcommand}" "invalid release stage" "$1"
|
|
}
|
|
|
|
die_cannot_remove_grp(){
|
|
std::die 3 "%s %s: %s -- %s\n" \
|
|
"${CMD}" "${subcommand}" "cannot remove group due to loaded modules" "$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_invalid_value(){
|
|
std::die 1 "%s %s: %s -- %s\n" \
|
|
"${CMD}" "${subcommand}" "invalid string for $1" "$2"
|
|
}
|
|
|
|
die_conflict(){
|
|
std::die 3 "%s %s: %s -- %b\n" \
|
|
"${CMD}" "${subcommand}" \
|
|
"module conflicts with already loaded modules" "$1"
|
|
}
|
|
|
|
die_cmd_failed(){
|
|
local error_txt='failed'
|
|
std::die 3 "%s %s: %s" \
|
|
"${CMD}" "${subcommand}" \
|
|
"failed"
|
|
}
|
|
|
|
die_cannot_use_overlay(){
|
|
std::die 3 "%s %s: %s -- %s" \
|
|
"${CMD}" "${subcommand}" \
|
|
"overlay cannot be added since some modules are already loaded!" "$1"
|
|
}
|
|
|
|
die_overlay_already_in_use(){
|
|
std::die 3 "%s %s: %s -- %s" \
|
|
"${CMD}" "${subcommand}" \
|
|
"overlay already in use" "$1"
|
|
}
|
|
|
|
die_not_a_modulefile(){
|
|
std::die 3 "%s %s: %s -- %s" \
|
|
"${CMD}" "${subcommand}" \
|
|
"not a modulefile" "$1"
|
|
}
|
|
|
|
die_invalid_collection_name(){
|
|
std::die 3 "%s %s: %s -- %s" \
|
|
"${CMD}" "${subcommand}" \
|
|
"invalid collection name" "$1"
|
|
}
|
|
|
|
die_cannot_create_directory(){
|
|
std::die 3 "%s %s: %s -- %s" \
|
|
"${CMD}" "${subcommand}" \
|
|
"cannot create directory" "$1"
|
|
}
|
|
|
|
die_cannot_save_collection(){
|
|
std::die 3 "%s %s: %s -- %s" \
|
|
"${CMD}" "${subcommand}" \
|
|
"cannot save_collection" "$1"
|
|
}
|
|
|
|
die_collection_doesnt_exist(){
|
|
std::die 3 "%s %s: %s -- %s" \
|
|
"${CMD}" "${subcommand}" \
|
|
"collection doesn't exist or isn't readable" "$1"
|
|
}
|
|
|
|
#
|
|
# get release stage of module
|
|
# Note:
|
|
# - the release stage of a module outside ${OverlayInfo[@]} is always 'stable'
|
|
# - the release stage of a module inside ${OverlayInfo[@]} without a
|
|
# coresponding file is always 'unstable'
|
|
#
|
|
# Args:
|
|
# $1 reference variable to return release stage
|
|
# $2 modulefile directory (element of MODULEPATH)
|
|
# $3 module name/version
|
|
#
|
|
get_release_stage() {
|
|
local -n grs_rel_stage="$1"
|
|
local -r dir="$2"
|
|
local -r modulefile="${dir}/$3"
|
|
|
|
if [[ ! -v Dir2OverlayMap[${dir%/${PMODULES_MODULEFILES_DIR}*}] ]]; then
|
|
grs_rel_stage='stable'
|
|
return
|
|
fi
|
|
#
|
|
# In an overlay the name of the module-file is something like
|
|
# dir/modulefiles/name/version
|
|
# the corresponding file is
|
|
# dir/modulefiles/name/.release-version
|
|
#
|
|
local -r rel_stage_file="${modulefile%/*}/.release-${modulefile##*/}"
|
|
if [[ -r ${rel_stage_file} ]]; then
|
|
# read file, remove empty lines, spaces etc
|
|
grs_rel_stage=$( < "${rel_stage_file}" )
|
|
else
|
|
grs_rel_stage='unstable'
|
|
fi
|
|
}
|
|
|
|
#
|
|
# check whether the argument in $1 is a valid release stage.
|
|
#
|
|
is_release_stage() {
|
|
[[ :${ReleaseStages}: =~ :$1: ]]
|
|
}
|
|
|
|
#
|
|
# Check whether a given moduledir is in an used overlay.
|
|
# If yes, return 0 otherwise return 1
|
|
#
|
|
# Arguments
|
|
# $1 ref.var to return overlay
|
|
# $2 ref.var to return group
|
|
# $3 moduledir to check
|
|
#
|
|
find_overlay () {
|
|
local -n fo_ol="$1"
|
|
local -n fo_group="$2"
|
|
local path="${3//+(\/)/\/}" # replace multpile '/' with one
|
|
path="${path/%\/}" # remove trailing slash if exist
|
|
path="${path%/${PMODULES_MODULEFILES_DIR}*}"
|
|
|
|
# return if not in an overlay
|
|
[[ -v Dir2OverlayMap[${path}] ]] || return 1
|
|
|
|
fo_ol="${Dir2OverlayMap[${path}]}"
|
|
fo_group="${path#${OverlayInfo[${ol}:mod_root]}/}"
|
|
return 0
|
|
}
|
|
|
|
#
|
|
# Check whether the module passed in argument $1 is loaded.
|
|
#
|
|
module_is_loaded() {
|
|
[[ :${LOADEDMODULES}: =~ :$1: ]]
|
|
}
|
|
|
|
#
|
|
# test whether the given file is a module file.
|
|
#
|
|
# - for lua: the extension must be .lua
|
|
# - for Tcl: check shebang
|
|
#
|
|
# return interpreter in reference variable
|
|
#
|
|
# Arguments:
|
|
# $1 [out] ref. variable for result
|
|
# $2 [in] full qualified file name to test
|
|
#
|
|
# Return value:
|
|
# 0 if file exist, is readable and is either a lua or Tcl module file
|
|
# 1 if file exist but is neither a lua or Tcl module file
|
|
# 2 if file doesn't exist
|
|
#
|
|
is_modulefile() {
|
|
local -n im_interp="$1"
|
|
local -r fname="$2"
|
|
|
|
# does file exist and is readable?
|
|
[[ -r ${fname} ]] || return 2
|
|
|
|
if [[ "${fname##*.}" == 'lua' ]]; then
|
|
im_interp='lmod'
|
|
return 0
|
|
fi
|
|
local -- shebang
|
|
read -n 11 shebang < "${fname}"
|
|
if [[ "${shebang:0:8}" == '#%Module' ]] \
|
|
|| [[ "${shebang:0:9}" == '#%Pmodule' ]]; then
|
|
im_interp='tcl'
|
|
return 0
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
#
|
|
# Get the value of <module>_PREFIX.
|
|
#
|
|
# Arguments:
|
|
# $1 reference variable to return result
|
|
# $2 modulefile
|
|
#
|
|
get_module_prefix() {
|
|
local -n _prefix="$1"
|
|
_prefix=$("${modulecmd}" bash show "$2" 2>&1 | \
|
|
${awk} '/_PREFIX |_HOME / {print $3; exit}')
|
|
}
|
|
|
|
#
|
|
# Generic wrappers of 'modulecmd':
|
|
#
|
|
# subcommand_generic0:
|
|
# no argument allowed
|
|
# subcommand_generic1:
|
|
# Exact one argument must be passed
|
|
# subcommand_generic1plus:
|
|
# One or more arguments must be passed
|
|
#
|
|
# The options to output help are always accepted.
|
|
#
|
|
subcommand_generic0() {
|
|
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_no_args_allowed
|
|
"${modulecmd}" "${Shell}" "${subcommand}"
|
|
}
|
|
|
|
subcommand_generic1() {
|
|
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_missing_arg
|
|
(( ${#args[@]} > 1 )) && \
|
|
die_too_many_args
|
|
"${modulecmd}" "${Shell}" "${subcommand}" "${args[@]}"
|
|
}
|
|
|
|
subcommand_generic1plus() {
|
|
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_missing_arg
|
|
"${modulecmd}" "${Shell}" "${subcommand}" "${args[@]}"
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# load [-fsvw] <module>
|
|
#
|
|
# $1: module to load
|
|
#
|
|
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 rel_stage='undef'
|
|
local current_modulefile=''
|
|
local prefix=''
|
|
local m=''
|
|
|
|
#......................................................................
|
|
# output load 'hints'
|
|
#
|
|
# Note:
|
|
# The variable 'm' from the parent function will be used
|
|
# but not changed.
|
|
#
|
|
# Args:
|
|
# none
|
|
get_load_hints() {
|
|
local -n output="$1"
|
|
local rel_stage=''
|
|
output=''
|
|
while read -a line; do
|
|
[[ -z ${line} ]] && continue
|
|
rel_stage=${line[1]}
|
|
if [[ ! ":${UsedReleaseStages}:" =~ "${rel_stage}" ]]; then
|
|
output+="module use ${rel_stage}; "
|
|
fi
|
|
local group=${line[2]}
|
|
if [[ ! ":${UsedGroups}:" =~ ":${group}:" ]] && \
|
|
(( ${GroupDepths[${group}]} == 0 )); then
|
|
output+="module use ${group}; "
|
|
fi
|
|
local -i n=$(( ${GroupDepths[${group}]}/2 ))
|
|
output+="module load ${line[@]:3:$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"
|
|
while read dep; do
|
|
[[ -z ${dep} ]] && continue
|
|
[[ ${dep:0:1} == \# ]] && continue
|
|
module_is_loaded "${dep}" && continue
|
|
subcommand_load "${dep}"
|
|
done < "${fname}"
|
|
}
|
|
|
|
#......................................................................
|
|
# Check whether argument is a group.
|
|
#
|
|
# In the following function we test whether the group exist in
|
|
# an overlay. It doesn't matter which overlay. If we find an
|
|
# overlay providing modules for this group, we compute the
|
|
# hierarchical depth of the group and save this value for later
|
|
# use.
|
|
#
|
|
# Args:
|
|
# $1: group
|
|
#
|
|
# Notes:
|
|
# This function is used with extended module names.
|
|
#
|
|
# Multiple overlays may provide modules for the same group. But the
|
|
# hierarchical depth of a group must always be the same.
|
|
#
|
|
is_group () {
|
|
find_overlay_with_group() {
|
|
local -n _ol="$1"
|
|
local -r group="$2/${PMODULES_MODULEFILES_DIR}"
|
|
local ol
|
|
for ol in "${UsedOverlays[@]}"; do
|
|
local inst_root="${OverlayInfo[${ol}:inst_root]}"
|
|
if [[ -d "${inst_root}/${group}" ]]; then
|
|
_ol="${ol}"
|
|
return 0
|
|
fi
|
|
done
|
|
return 1
|
|
}
|
|
|
|
local -r group="$1"
|
|
# arg isn't emtpy and group already in cache
|
|
[[ -n ${group} ]] && [[ -n ${GroupDepths[${group}]} ]] && return 0
|
|
|
|
local ol=''
|
|
find_overlay_with_group ol "${group}" || return 1
|
|
local moduledir="${OverlayInfo[${ol}:mod_root]}/${group}/${PMODULES_MODULEFILES_DIR}"
|
|
local -i depth
|
|
compute_group_depth depth "${moduledir}" || return 1
|
|
GroupDepths[${group}]=${depth}
|
|
g_env_must_be_saved='yes'
|
|
}
|
|
|
|
#......................................................................
|
|
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_missing_arg
|
|
|
|
IFS=':' read -r -a modulepath <<< "${MODULEPATH}"
|
|
|
|
local m=''
|
|
for m in "${args[@]}"; do
|
|
if [[ "$m" =~ ":" ]]; then
|
|
|
|
# extendet module name is either
|
|
# - group:name or
|
|
# - group:name:rel_stage or
|
|
# - rel_stage:name or
|
|
# - rel_stage:group:name or
|
|
# - name:rel_stage
|
|
|
|
|
|
IFS=':'
|
|
local -a toks=($m)
|
|
unset IFS
|
|
local group=''
|
|
local rel_stage=''
|
|
if is_group "${toks[0]}"; then
|
|
group=${toks[0]}
|
|
m=${toks[1]}
|
|
rel_stage=${toks[2]}
|
|
elif is_release_stage "${toks[0]}"; then
|
|
rel_stage=${toks[0]}
|
|
if is_group "${toks[1]}"; then
|
|
group=${toks[1]}
|
|
m=${toks[2]}
|
|
else
|
|
m=${toks[1]}
|
|
group=${toks[2]}
|
|
fi
|
|
else
|
|
m=${toks[0]}
|
|
if is_group "${toks[1]}"; then
|
|
group=${toks[1]}
|
|
rel_stage=${toks[2]}
|
|
else
|
|
rel_stage=${toks[1]}
|
|
group=${toks[2]}
|
|
fi
|
|
fi
|
|
if [[ -n ${group} ]]; then
|
|
is_group "${group}" || \
|
|
die_illegal_group "${group}"
|
|
local -i depth=${GroupDepths[${group}]}
|
|
(( depth != 0 )) && \
|
|
die_illegal_group "${group}"
|
|
group+="/${PMODULES_MODULEFILES_DIR}"
|
|
for overlay in "${UsedOverlays[@]}"; do
|
|
local mod_root="${OverlayInfo[${overlay}:mod_root]}"
|
|
modulepath=( "${mod_root}/${group}" "${modulepath[@]}" )
|
|
done
|
|
fi
|
|
if [[ -n ${rel_stage} ]]; then
|
|
is_release_stage "${rel_stage}" || \
|
|
die_illegal_rel_stage "${rel_stage}"
|
|
std::append_path UsedReleaseStages "${rel_stage}"
|
|
g_env_must_be_saved='yes'
|
|
fi
|
|
fi # handle extended module names
|
|
local moduledir=''
|
|
find_modulefile current_modulefile rel_stage moduledir "${m}" "${modulepath[@]}"
|
|
if [[ -z ${current_modulefile} ]]; then
|
|
local hints=''
|
|
get_load_hints hints
|
|
if [[ -z "${hints}" ]]; then
|
|
die_module_nexist "${m}"
|
|
else
|
|
die_module_unavail "${m}${hints}"
|
|
fi
|
|
fi
|
|
[[ ${m} == Pmodules/* ]] && [[ -n ${LOADEDMODULES} ]] && \
|
|
die_conflict "${m}"
|
|
# continue if already loaded
|
|
[[ ":${LOADEDMODULES}:" =~ ":${m}:" ]] && continue
|
|
|
|
local interp=''
|
|
is_modulefile interp "${current_modulefile}" || \
|
|
die_not_a_modulefile "${m}"
|
|
local prefix=''
|
|
get_module_prefix prefix "${current_modulefile}"
|
|
if [[ -n ${prefix} ]]; then
|
|
test -r "${prefix}/.info" && cat "$_" 1>&2
|
|
test -r "${prefix}/.dependencies" && load_dependencies "$_"
|
|
fi
|
|
|
|
if [[ "${interp}" == 'lmod' ]]; then
|
|
current_modulefile="${current_modulefile/${moduledir}\/}"
|
|
modulecmd="${lmod_cmd}"
|
|
else
|
|
modulecmd="${tcl_cmd}"
|
|
fi
|
|
local output=$("${modulecmd}" 'bash' ${opts} 'load' \
|
|
"${current_modulefile}" 2> "${tmpfile}")
|
|
|
|
# we do not want to print the error message we got from
|
|
# modulecmd, they are a bit ugly
|
|
# :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.
|
|
|
|
local error=$( < "${tmpfile}")
|
|
if [[ "${error}" =~ ":ERROR:" ]]; then
|
|
local s=${error%%$'\n'*}
|
|
[[ "$s" =~ ' conflicts ' ]] && \
|
|
die_conflict "${m}"
|
|
die_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
|
|
eval "${output}"
|
|
if [[ -n "${error}" ]]; then
|
|
echo "${error}" 1>&2
|
|
fi
|
|
local msg=''
|
|
if [[ ${verbosity_lvl} != silent ]] && \
|
|
[[ ${rel_stage} != stable ]]; then
|
|
msg=$(printf "%s %s: %s -- %s" \
|
|
"${CMD}" 'load' \
|
|
"${rel_stage} module has been loaded" \
|
|
"${m}")
|
|
std::info "%s" "${msg}"
|
|
fi
|
|
msg=$(printf "%s: %s %s %s" \
|
|
'load' \
|
|
"modulefile=${current_modulefile}" \
|
|
"rel-stage=${rel_stage}" \
|
|
"user=${USER}")
|
|
${logger} -t Pmodules "${msg}"
|
|
done
|
|
# fix LOADEDMODULES
|
|
LOADEDMODULES="${_LMFILES_}"
|
|
local dir
|
|
while read dir; do
|
|
# if the first or last character of MODULEPATH is ':',
|
|
# we read an empty string.
|
|
[[ -z ${dir} ]] && continue
|
|
[[ "${dir: -1}" == "/" ]] || dir+="/"
|
|
LOADEDMODULES="${LOADEDMODULES//${dir}}"
|
|
done <<< "${MODULEPATH//:/$'\n'}"
|
|
g_env_must_be_saved='yes'
|
|
export_env 'LOADEDMODULES'
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# unload
|
|
#
|
|
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() {
|
|
# :FIXME: add dependency tests: don't 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_illegal_opt "$1"
|
|
;;
|
|
|
|
* )
|
|
args+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
(( ${#args[@]} == 0 )) && \
|
|
die_missing_arg
|
|
|
|
# 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}"
|
|
|
|
IFS=: read -r -a _lmfiles_ <<< "${_LMFILES_}"
|
|
local arg
|
|
local lmfile
|
|
for arg in "${args[@]}"; do
|
|
for lmfile in "${_lmfiles_[@]}" '_zzzz_'; do
|
|
if [[ $lmfile =~ ${arg} ]]; then
|
|
break
|
|
fi
|
|
done
|
|
if [[ "${lmfile}" == '_zzzz_' ]]; then
|
|
continue
|
|
fi
|
|
local interp
|
|
is_modulefile interp "${lmfile}" || die_not_a_modulefile "${m}"
|
|
if [[ "${interp}" == 'lmod' ]]; then
|
|
current_modulefile="${current_modulefile/${moduledir}\/}"
|
|
modulecmd="${lmod_cmd}"
|
|
else
|
|
modulecmd="${tcl_cmd}"
|
|
fi
|
|
|
|
local output=$("${modulecmd}" "${Shell}" 'unload' "${arg}")
|
|
eval "$(echo "${output}"|${sed} -e 's/;unalias [^;]*//g')"
|
|
case ${Shell} in
|
|
sh | bash | zsh )
|
|
echo "${output}"
|
|
;;
|
|
* )
|
|
"${modulecmd}" "${Shell}" 'unload' "${arg}"
|
|
;;
|
|
esac
|
|
done
|
|
if [[ -z ${PMODULES_HOME} ]]; then
|
|
PMODULES_HOME="${saved_home}"
|
|
export_env 'PMODULES_HOME'
|
|
fi
|
|
g_env_must_be_saved='yes'
|
|
} # subcommand_unload
|
|
|
|
##############################################################################
|
|
#
|
|
# swap <module> [<module>]
|
|
#
|
|
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 args=()
|
|
while (( $# > 0 )); do
|
|
case $1 in
|
|
-\? | -H | --help )
|
|
print_help "${subcommand}"
|
|
;;
|
|
-- )
|
|
shift 1
|
|
args+=( "$@" )
|
|
break
|
|
;;
|
|
* )
|
|
args+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
(( ${#args[@]} == 0 )) && \
|
|
die_missing_arg
|
|
(( ${#args[@]} > 2 )) && \
|
|
die_too_many_args
|
|
|
|
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}"
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# show <module>
|
|
#
|
|
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 args=()
|
|
while (( $# > 0 )); do
|
|
case $1 in
|
|
-\? | -H | --help )
|
|
print_help "${subcommand}"
|
|
;;
|
|
-- )
|
|
shift 1
|
|
args+=( "$@" )
|
|
break
|
|
;;
|
|
* )
|
|
args+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
(( ${#args[@]} == 0 )) && \
|
|
die_missing_arg
|
|
|
|
local arg
|
|
for arg in "${args[@]}"; do
|
|
"${modulecmd}" "${Shell}" "${subcommand}" "${arg}"
|
|
done
|
|
}
|
|
|
|
###############################################################################
|
|
# Find all modules in a given modulepath matching a specific string.
|
|
# The search can be restricted to certain release stages.
|
|
#
|
|
# Args:
|
|
# $1 reference variable to return result
|
|
# $2 search pattern
|
|
# $3 release stages
|
|
# $4... module path (fully qualified directory names)
|
|
#
|
|
# return list like
|
|
# modulename_1 rel_stage_1 modulefile_1 ...
|
|
#
|
|
get_available_modules() {
|
|
local -n gam_mods="$1"
|
|
local -r module="$2"
|
|
local -r used_rel_stages="${3:-${UsedReleaseStages}}"
|
|
shift 3 # in the for loop below we use $@ to loop over module path
|
|
local rel_stage
|
|
|
|
local -A dict
|
|
local -A modulenames
|
|
local dir
|
|
gam_mods=()
|
|
# loop over all entries in given module path
|
|
for dir in "$@"; do
|
|
test -d "${dir}" || continue
|
|
cd "${dir}"
|
|
# find overlay and group for this directory
|
|
local ol=''
|
|
local group=''
|
|
find_overlay ol group "${dir}"
|
|
|
|
# if directory is empty continue
|
|
local -a dir_entries=(*)
|
|
(( ${#dir_entries[@]} > 0 )) || continue
|
|
|
|
# loop over all files (and sym-link) in this directory and
|
|
# its sub-directories
|
|
local mod='' # module_name/module_version
|
|
while read mod; do
|
|
local add='no'
|
|
if [[ -n "${ol}" ]]; 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
|
|
local name="${mod%/*}"
|
|
if [[ -z "${modulenames[${name}]}" ]]; then
|
|
if [[ "${OverlayInfo[${ol}:type]}" == "${ol_hiding}" ]]; then
|
|
modulenames[${name}]="${ol}"
|
|
else
|
|
modulenames[${name}]='0'
|
|
fi
|
|
add='yes'
|
|
elif [[ "${modulenames[${name}]}" == "${ol}" ]]; then
|
|
add='yes'
|
|
elif [[ "${modulenames[${name}]}" == '0' ]] \
|
|
&& [[ -z ${dict[${mod}]} ]]; then
|
|
add='yes'
|
|
fi
|
|
else
|
|
add='yes' # module is NOT in an overlay
|
|
fi
|
|
[[ "${add}" == 'no' ]] && continue
|
|
get_release_stage rel_stage "${dir}" "${mod}"
|
|
[[ :${used_rel_stages}: =~ :${rel_stage}: ]] || continue
|
|
|
|
gam_mods+=( "${mod}" ${rel_stage} "${dir}/${mod}" "${ol}" )
|
|
dict[${mod}]=1
|
|
done < <(${find} -L "${dir_entries[@]}" \
|
|
\( -type f -o -type l \) \
|
|
-not -name ".*" \
|
|
-ipath "${module}*" \
|
|
| ${sort} --version-sort)
|
|
done
|
|
} # get_available_modules()
|
|
|
|
#
|
|
# find module(file) to load. Input arguments are
|
|
# $1 [out] ref. variable to return module file
|
|
# $2 [out] ref. variable to return release stage
|
|
# $3 |in] module to load
|
|
# $4 [in] module search path (usually splitted MODULEPATH)
|
|
#
|
|
# The module name can be
|
|
# name
|
|
# name/version
|
|
# name/version_flag
|
|
#
|
|
# Return
|
|
# 0 if a modulefile has been found
|
|
# != else
|
|
#
|
|
find_modulefile() {
|
|
local -n fm_modulefile="$1"
|
|
local -n fm_rel_stage="$2"
|
|
local -n fm_dir="$3"
|
|
local -r module="$4"
|
|
local -a dirs=("${@:5}")
|
|
|
|
local dir
|
|
for dir in "${dirs[@]}"; do
|
|
test -d "${dir}" || continue
|
|
local -i col=$((${#dir} + 2 ))
|
|
local -a modules
|
|
if [[ ${module} == */* ]]; then
|
|
# a version number has been specified. But we still might
|
|
# have the same module/version with different use flags.
|
|
# The different release stages we ignore in this case.
|
|
modules=( $(${find} -L "${dir}" -type f -not -name ".*" \
|
|
-ipath "${dir}/${module}*" \
|
|
| cut -b${col}-) )
|
|
for mod in "${modules[@]}"; do
|
|
if [[ ${mod} == ${module} ]]; then
|
|
:
|
|
elif [[ ${mod} == ${module}.lua ]]; then
|
|
mod="${module}.lua"
|
|
else
|
|
continue
|
|
fi
|
|
get_release_stage \
|
|
fm_rel_stage \
|
|
"${dir}" \
|
|
"${mod}"
|
|
|
|
fm_modulefile="${dir}/${mod}"
|
|
fm_dir="${dir}"
|
|
return 0
|
|
done
|
|
else
|
|
# no version has been specified. This makes it more
|
|
# difficult. We have to load the newest version taking
|
|
# the used release stages and flags into account.
|
|
|
|
# get list of reverse sorted version numbers
|
|
(( col += ${#module} + 1 ))
|
|
modules=( $(${find} -L "${dir}" -type f -not -name ".*" \
|
|
-ipath "${dir}/${module}/*" \
|
|
| cut -b${col}- \
|
|
| sort -rV ) )
|
|
# prepend module name
|
|
modules=( "${modules[@]/#/${module}/}" )
|
|
|
|
# now modules contains a reverse sorted list of
|
|
# available modules in the form name/version
|
|
for mod in "${modules[@]}"; do
|
|
if [[ ${mod} == ${module}/* ]]; then
|
|
:
|
|
else
|
|
continue
|
|
fi
|
|
get_release_stage \
|
|
fm_rel_stage \
|
|
"${dir}" \
|
|
"${mod}"
|
|
# don't load modules with unused release stages
|
|
if [[ :${fm_rel_stage}: =~ :${UsedReleaseStages}: ]]; then
|
|
fm_modulefile="${dir}/${mod}"
|
|
fm_dir="${dir}"
|
|
return 0
|
|
fi
|
|
done
|
|
fi
|
|
done
|
|
return 1
|
|
} # find_modulefile()
|
|
|
|
##############################################################################
|
|
#
|
|
# avail [-hlt] [<module-pattern>...]
|
|
#
|
|
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() {
|
|
# use this variable in the output functions
|
|
local -a mods=()
|
|
local dir=''
|
|
|
|
# get number of columns of terminal
|
|
cols=$(tput cols)
|
|
|
|
#......................................................................
|
|
output_header() {
|
|
local caption="$1"
|
|
let 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() {
|
|
output_header "$1"
|
|
local -i i=0
|
|
for (( i=0; i<${#mods[@]}; i+=4 )); do
|
|
local mod=${mods[i]}
|
|
local rel_stage=${mods[i+1]}
|
|
case ${rel_stage} in
|
|
stable )
|
|
out=''
|
|
;;
|
|
* )
|
|
out="${rel_stage}"
|
|
;;
|
|
esac
|
|
printf "%-20s\t%s\n" "${mod}" "${out}" 1>&2
|
|
done
|
|
std::info ""
|
|
}
|
|
|
|
#......................................................................
|
|
machine_output() {
|
|
for (( i=0; i<${#mods[@]}; i+=4 )); 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() {
|
|
output_header "$1"
|
|
for (( i=0; i<${#mods[@]}; i+=4 )); do
|
|
local mod=${mods[i]}
|
|
local rel_stage=${mods[i+1]}
|
|
case ${rel_stage} in
|
|
stable )
|
|
out=''
|
|
;;
|
|
* )
|
|
out=${rel_stage}
|
|
;;
|
|
esac
|
|
printf "%-20s\t%s\n" "${mod}" "${out}" 1>&2
|
|
done
|
|
std::info ""
|
|
}
|
|
|
|
#......................................................................
|
|
human_readable_output() {
|
|
output_header "$1"
|
|
|
|
local -a available_modules=()
|
|
local mod=''
|
|
local -i max_length=1
|
|
for ((i=0; i<${#mods[@]}; i+=4)); do
|
|
if [[ ${verbosity_lvl} == 'verbose' ]]; then
|
|
local rel_stage=${mods[i+1]}
|
|
case ${rel_stage} in
|
|
stable )
|
|
mod="${mods[i]}"
|
|
;;
|
|
* )
|
|
mod="${mods[i]}(${rel_stage:0:1})"
|
|
;;
|
|
esac
|
|
else
|
|
mod=${mods[i]}
|
|
fi
|
|
local -i n=${#mod}
|
|
(( n > max_length )) && (( max_length=n ))
|
|
available_modules+=("${mod}")
|
|
done
|
|
IFS=$'\n' available_modules=($(sort --version-sort <<<"${available_modules[*]}"))
|
|
unset IFS
|
|
local -i span=$(( max_length / 16 + 1 )) # compute column size
|
|
local -i colsize=$(( span * 16 )) # as multiple of 16
|
|
local -i column=$cols # force a line-break
|
|
for mod in "${available_modules[@]}"; do
|
|
local -i len=${#mod}
|
|
if (( column+len >= cols )); then
|
|
printf -- "\n" 1>&2
|
|
column=0
|
|
fi
|
|
if (( column+colsize < cols )); then
|
|
fmt="%-${colsize}s"
|
|
else
|
|
fmt="%-s"
|
|
fi
|
|
printf "${fmt}" "${mod}" 1>&2
|
|
column+=colsize
|
|
done
|
|
printf -- "\n\n" 1>&2
|
|
}
|
|
|
|
#......................................................................
|
|
local pattern=()
|
|
local output_function='human_readable_output'
|
|
local opt_use_rel_stages="${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_rel_stages="${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
|
|
IFS=':'
|
|
local -a modulepath=(${MODULEPATH})
|
|
unset IFS
|
|
local dir
|
|
local group=''
|
|
local groups=()
|
|
local ol=''
|
|
local name=''
|
|
for dir in "${modulepath[@]}"; do
|
|
find_overlay ol group "${dir}"
|
|
if (( $? == 0 )); then
|
|
name="${group}"
|
|
else
|
|
name="${dir}"
|
|
group="${dir//[\/ .-]/_}"
|
|
fi
|
|
if [[ ! -v modulepath_${group} ]]; then
|
|
typeset -a modulepath_${group}
|
|
fi
|
|
# with overlays we can have multiple directories per group!
|
|
typeset -n path=modulepath_${group}
|
|
path+=("${dir}")
|
|
local -i found=0
|
|
for group in "${groups[@]}"; do
|
|
if [[ "${group}" == ${name} ]]; then
|
|
found=1
|
|
fi
|
|
done
|
|
(( found == 0 )) && groups+=( "${name}" )
|
|
done
|
|
local p
|
|
for string in "${pattern[@]}"; do
|
|
for group in "${groups[@]}"; do
|
|
if (( ${#opt_groups[@]} > 0 )) && [[ ! -v opt_groups[${group}] ]]; then
|
|
continue
|
|
fi
|
|
local ref="${group//[\/ .-]/_}"
|
|
[[ -v modulepath_${ref} ]] || continue
|
|
typeset -n path=modulepath_${ref}
|
|
get_available_modules \
|
|
mods \
|
|
"${string}*" \
|
|
"${opt_use_rel_stages}" \
|
|
"${path[@]}"
|
|
|
|
[[ ${#mods[@]} == 0 ]] && continue
|
|
${output_function} "${group}"
|
|
done
|
|
done
|
|
} # subcommand_avail()
|
|
|
|
##############################################################################
|
|
#
|
|
# use [-a|--append|-p|--prepend] [directory|group|release_stage...]
|
|
#
|
|
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.
|
|
"
|
|
|
|
subcommand_use() {
|
|
IFS=':'
|
|
local -a modulepath=(${MODULEPATH})
|
|
unset IFS
|
|
local add2path_func='std::append_path'
|
|
|
|
#......................................................................
|
|
group_is_used() {
|
|
[[ :${UsedGroups}: =~ :$1: ]]
|
|
}
|
|
|
|
#......................................................................
|
|
print_info() {
|
|
print_ol_info(){
|
|
local only_used="$1"
|
|
local ol=''
|
|
for ol in "${Overlays[@]}"; do
|
|
[[ ${OverlayInfo[${ol}:used]} == ${only_used} ]] || continue
|
|
local inst_root="${OverlayInfo[${ol}:inst_root]}"
|
|
local mod_root="${OverlayInfo[${ol}:mod_root]}"
|
|
local txt="\t${ol}"
|
|
if [[ ${inst_root} == ${mod_root} ]]; then
|
|
txt+="\n\t\t${inst_root}"
|
|
else
|
|
txt+="\n\t\t${inst_root} (install root)"
|
|
txt+="\n\t\t${mod_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:"
|
|
let n=0
|
|
local group
|
|
for (( i=0; i<${#modulepath[@]}; i++)); do
|
|
if ! find_overlay ol group "${modulepath[i]}"; then
|
|
std::info "\t${modulepath[i]}"
|
|
let n+=1
|
|
fi
|
|
done
|
|
if (( n == 0 )); then
|
|
std::info "\tnone"
|
|
fi
|
|
std::info ""
|
|
} # print_info
|
|
|
|
#......................................................................
|
|
use () {
|
|
use_overlay() {
|
|
local ol_name="$1"
|
|
|
|
[[ -n "${LOADEDMODULES}" ]] && \
|
|
[[ "${LOADEDMODULES}" != Pmodules/+([.0-9rc]) ]] && \
|
|
die_cannot_use_overlay "${ol_name}"
|
|
|
|
[[ ${OverlayInfo[${ol_name}:used]} == 'yes' ]] && return 0
|
|
# die_overlay_already_in_use "${ol_name}"
|
|
|
|
if [[ "${OverlayInfo[${ol_name}:type]}" == "${ol_replacing}" ]]; then
|
|
# if this overlay replaces groups, we have
|
|
# to remove the modules made available by
|
|
# other overlays in these groups
|
|
for group in ${UsedGroups//:/ }; do
|
|
# is this group in the to be added overlay?
|
|
local dir="${OverlayInfo[${ol_name}:mod_root]}/"
|
|
dir+="${group}/${PMODULES_MODULEFILES_DIR}"
|
|
[[ -d "${dir}" ]] || continue # no
|
|
|
|
dir="/${group}/${PMODULES_MODULEFILES_DIR}"
|
|
local -a dirs=()
|
|
for ol in "${UsedOverlays[@]}"; do
|
|
dirs+=( ${OverlayInfo[${ol}:mod_root]}${dir} )
|
|
done
|
|
std::remove_path MODULEPATH "${dirs[@]}"
|
|
done
|
|
fi
|
|
scan_groups "${ol_name}"
|
|
for group in ${UsedGroups//:/ }; do
|
|
local dir="${OverlayInfo[${ol_name}:mod_root]}/"
|
|
dir+="${group}/${PMODULES_MODULEFILES_DIR}"
|
|
if [[ -d "${dir}" ]]; then
|
|
std::prepend_path MODULEPATH "${dir}"
|
|
fi
|
|
done
|
|
|
|
UsedOverlays=( "${ol_name}" "${UsedOverlays[@]}" )
|
|
OverlayInfo[${ol_name}:used]='yes'
|
|
export_env UsedOverlays
|
|
g_env_must_be_saved='yes'
|
|
scan_groups "${UsedOverlays[@]}"
|
|
}
|
|
|
|
#..............................................................
|
|
use_group() {
|
|
# die if argument is a hierarchical group
|
|
(( ${GroupDepths[${arg}]} > 0 )) && \
|
|
die_illegal_group "${arg}"
|
|
|
|
std::append_path UsedGroups "$1"
|
|
local ol_name
|
|
for ol_name in "${UsedOverlays[@]}"; do
|
|
local dir="${OverlayInfo[${ol_name}:mod_root]}/$1/${PMODULES_MODULEFILES_DIR}"
|
|
[[ -d "${dir}" ]] || continue
|
|
|
|
std::prepend_path MODULEPATH "${dir}"
|
|
[[ "${OverlayInfo[${ol_name}:type]}" == "${ol_replacing}" ]] && break
|
|
done
|
|
}
|
|
|
|
#..............................................................
|
|
local arg=$1
|
|
|
|
if is_release_stage "${arg}"; then
|
|
# argument is release stage
|
|
std::append_path UsedReleaseStages "${arg}"
|
|
return $?
|
|
fi
|
|
if [[ -v OverlayInfo[${arg}:type] ]]; then
|
|
use_overlay "${arg}"
|
|
return $?
|
|
fi
|
|
if [[ -d ${arg} ]]; then
|
|
local dir=$(cd "${arg}" && pwd -L)
|
|
${add2path_func} MODULEPATH "${dir}"
|
|
return $?
|
|
fi
|
|
if [[ ! -v GroupDepths[${arg}] ]]; then
|
|
# this scan is required if a new group has been
|
|
# create inside an used overlay
|
|
scan_groups "${UsedOverlays[@]}"
|
|
g_env_must_be_saved='yes'
|
|
fi
|
|
if [[ -n ${GroupDepths[${arg}]} ]]; then
|
|
use_group "${arg}"
|
|
return $?
|
|
fi
|
|
die_invalid_value "use flag, group, overlay or directory" \
|
|
"${arg}"
|
|
} # use ()
|
|
|
|
#......................................................................
|
|
local -a args=()
|
|
while (( $# > 0)); do
|
|
case "$1" in
|
|
-\? | -H | --help )
|
|
print_help "${subcommand}"
|
|
;;
|
|
-a | --append )
|
|
add2path_func='std::append_path'
|
|
;;
|
|
-p | --prepend )
|
|
add2path_func='std::prepend_path'
|
|
;;
|
|
-- )
|
|
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
|
|
g_env_must_be_saved='yes'
|
|
export_env 'MODULEPATH'
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# unuse directory|group|release_stage|...
|
|
#
|
|
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() {
|
|
IFS=':'
|
|
local -a modulepath=(${MODULEPATH})
|
|
unset IFS
|
|
|
|
#......................................................................
|
|
unuse() {
|
|
#..............................................................
|
|
unuse_overlay() {
|
|
local ol_name="$1"
|
|
|
|
if [[ -n "${LOADEDMODULES}" ]] && \
|
|
[[ "${LOADEDMODULES}" != Pmodules/+([.0-9rc]) ]]; then
|
|
std::die 3 "%s %s: %s %s" \
|
|
"${CMD}" "${subcommand}" \
|
|
"overlay cannot be removed since" \
|
|
"some modules are still loaded!"
|
|
fi
|
|
|
|
[[ "${OverlayInfo[ol_name]:mod_root}" == "${PMODULES_HOME%%/Tools*}" ]] && \
|
|
std::die 3 "%s %s: %s -- %s" \
|
|
"${CMD}" "${subcommand}" \
|
|
"cannot remove base overlay" \
|
|
"${ol_name}"
|
|
[[ ${OverlayInfo[${ol_name}:used]} != 'yes' ]] && \
|
|
std::die 3 "%s %s: %s -- %s" \
|
|
"${CMD}" "${subcommand}" \
|
|
"not an used overlay" \
|
|
"${ol_name}"
|
|
# make sure first index is '0' (it should, but you never know)
|
|
UsedOverlays=( "${UsedOverlays[@]}" )
|
|
[[ "${ol_name}" != "${UsedOverlays[0]}" ]] && \
|
|
std::die 3 "%s %s: %s %s -- %s" \
|
|
"${CMD}" "${subcommand}" \
|
|
"overlay cannot be removed since" \
|
|
"it not on top of the stack" \
|
|
"${ol_name}"
|
|
|
|
if [[ "${OverlayInfo[${ol_name}:type]}" == "${ol_replacing}" ]]; then
|
|
# if this overlay hides groups, we have to re-add
|
|
# the modules made available by other overlays
|
|
local mod_root=${OverlayInfo[${ol_name}:mod_root]}
|
|
for group in ${UsedGroups//:/ }; do
|
|
# is this group in the to be removed overlay?
|
|
local dir="${mod_root}/${group}/${PMODULES_MODULEFILES_DIR}"
|
|
[[ -d "${dir}" ]] || continue # no
|
|
|
|
dir="/${group}/${PMODULES_MODULEFILES_DIR}"
|
|
std::prepend_path MODULEPATH "${UsedOverlays[@]/%/${dir}}"
|
|
done
|
|
fi
|
|
|
|
OverlayInfo[${ol_name}:used]='no'
|
|
UsedOverlays=( "${UsedOverlays[@]:1}")
|
|
g_env_must_be_saved='yes'
|
|
export_env UsedOverlays
|
|
local dir
|
|
for dir in "${modulepath[@]}"; do
|
|
[[ "${dir}" =~ "${OverlayInfo[${ol_name}:mod_root]}" ]] && \
|
|
std::remove_path MODULEPATH "${dir}"
|
|
done
|
|
}
|
|
|
|
#..............................................................
|
|
unuse_group() {
|
|
if (( ${GroupDepths[${arg}]} > 0 )); then
|
|
# argument is a hierarchical group in our root
|
|
std::die 3 "%s %s: %s -- %s" \
|
|
"${CMD}" "${subcommand}" \
|
|
"illegal group" \
|
|
"${arg}"
|
|
fi
|
|
|
|
local var="PMODULES_LOADED_${arg^^}"
|
|
if [[ -n "${!var}" ]]; then
|
|
std::die 3 "%s %s: %s -- %s" \
|
|
"${CMD}" "${subcommand}" \
|
|
"cannot remove group due to loaded modules" \
|
|
"${arg}"
|
|
fi
|
|
std::remove_path UsedGroups "${arg}"
|
|
local overlay
|
|
for overlay in "${UsedOverlays[@]}"; do
|
|
local dir="${overlay}/${arg}/${PMODULES_MODULEFILES_DIR}"
|
|
std::remove_path MODULEPATH "${dir}"
|
|
done
|
|
}
|
|
|
|
#..............................................................
|
|
local arg=$1
|
|
|
|
if is_release_stage "${arg}"; then
|
|
# argument is release stage
|
|
std::remove_path UsedReleaseStages "${arg}"
|
|
return
|
|
fi
|
|
if [[ -v OverlayInfo[${arg}:type] ]]; then
|
|
unuse_overlay "${arg}"
|
|
return 0
|
|
fi
|
|
if [[ -d ${arg} ]]; then
|
|
local dir=$(cd "${arg} && pwd -L")
|
|
std::remove_path MODULEPATH "${dir}"
|
|
fi
|
|
if [[ -n ${GroupDepths[${arg}]} ]]; then
|
|
unuse_group "${arg}"
|
|
return
|
|
fi
|
|
|
|
std::die 3 "%s %s: %s -- %s" \
|
|
"${CMD}" "${subcommand}" \
|
|
"invalid keyword, group, overlay or directory" \
|
|
"${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
|
|
if (( ${#args[@]} == 0 )); then
|
|
std::die 3 "%s %s: %s" \
|
|
"${CMD}" "${subcommand}" \
|
|
'missing argument'
|
|
fi
|
|
for arg in "${args[@]}"; do
|
|
unuse "${args[@]}"
|
|
done
|
|
g_env_must_be_saved='yes'
|
|
export_env 'MODULEPATH'
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# update
|
|
#
|
|
# :FIXME:
|
|
# either compile Modules with --enable-beginenv or remove the
|
|
# sub-command
|
|
#
|
|
Subcommands[update]='update'
|
|
Options[update]='-o \?H -l help'
|
|
Help[update]='
|
|
USAGE:
|
|
module update
|
|
Attempt to reload all loaded modulefiles.
|
|
'
|
|
|
|
subcommand_update() {
|
|
subcommand_generic0 "$@"
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# refresh
|
|
#
|
|
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() {
|
|
subcommand_generic0 "$@"
|
|
}
|
|
|
|
#
|
|
# Helper functions, used during initialization and for purging all modules.
|
|
#
|
|
# Arguments:
|
|
# none
|
|
#
|
|
init_modulepath() {
|
|
declare -gx MODULEPATH=''
|
|
local group
|
|
local ol
|
|
for ol in "${UsedOverlays[@]}"; do
|
|
for group in ${UsedGroups//:/ }; do
|
|
local dir="${OverlayInfo[${ol}:mod_root]}/${group}/${PMODULES_MODULEFILES_DIR}"
|
|
if [[ -d "${dir}" ]]; then
|
|
std::prepend_path MODULEPATH "${dir}"
|
|
fi
|
|
done
|
|
done
|
|
}
|
|
|
|
pmodules_init() {
|
|
init_used_groups() {
|
|
declare -gx UsedGroups=''
|
|
local group
|
|
for group in ${DefaultGroups//:/ }; do
|
|
std::append_path UsedGroups "${group}"
|
|
done
|
|
g_env_must_be_saved='yes'
|
|
}
|
|
init_used_releases() {
|
|
declare -g UsedReleaseStages=''
|
|
for r in ${DefaultReleaseStages//:/ }; do
|
|
std::append_path UsedReleaseStages "${r}"
|
|
done
|
|
g_env_must_be_saved='yes'
|
|
}
|
|
init_overlay_vars() {
|
|
declare -ag UsedOverlays=( 'base' )
|
|
OverlayInfo['base:used']='yes'
|
|
}
|
|
|
|
init_manpath() {
|
|
std::replace_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 [[ -n ${_manconf} ]]; then
|
|
while read name value rest; do
|
|
std::append_path MANPATH "${value}"
|
|
done < <(grep "^MANPATH\s" "${_manconf}")
|
|
unset _manconf
|
|
else
|
|
std::append_path MANPATH "${PMODULES_HOME}/share/man"
|
|
std::append_path MANPATH "/usr/share/man"
|
|
fi
|
|
}
|
|
|
|
pm::read_config
|
|
|
|
declare -gx LOADEDMODULES=''
|
|
declare -gx _LMFILES_=''
|
|
declare -Ag GroupDepths=()
|
|
declare -g Version="${PMODULES_VERSION}"
|
|
|
|
init_used_groups
|
|
init_used_releases
|
|
init_overlay_vars
|
|
init_modulepath
|
|
init_manpath
|
|
scan_groups "${UsedOverlays[@]}"
|
|
save_env
|
|
export_env \
|
|
LOADEDMODULES \
|
|
_LMFILES_ \
|
|
MODULEPATH \
|
|
PATH \
|
|
MANPATH
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# purge
|
|
#
|
|
Subcommands[purge]='purge'
|
|
Options[purge]='-o \?H -l help'
|
|
Help[purge]='
|
|
USAGE:
|
|
module purge
|
|
Unload all loaded modulefiles.
|
|
'
|
|
|
|
subcommand_purge() {
|
|
#
|
|
# unload all loaded modules
|
|
#
|
|
# 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_illegal_opt "$1"
|
|
;;
|
|
* )
|
|
args+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
(( ${#args[@]} > 0 )) && die_no_args_allowed
|
|
|
|
# get list of loaded modules with stripped MODULEPATH
|
|
IFS=':' read -r -a modules <<< "${LOADEDMODULES}"
|
|
|
|
for (( i=${#modules[@]}-1; i>=0; i-- )); do
|
|
[[ ${modules[$i]} == Pmodules/* ]] && continue
|
|
subcommand_unload "${modules[$i]}"
|
|
done
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# list [-hlt]
|
|
#
|
|
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() {
|
|
|
|
human_readable_output(){
|
|
# get list of loaded modules with stripped MODULEPATH
|
|
IFS=':' read -r -a modules <<< "${LOADEDMODULES}"
|
|
|
|
local strs=()
|
|
local -i n=1
|
|
local -i colsize=0
|
|
for module in "${modules[@]}"; do
|
|
local s=$(printf "%2d) %-s" $n "${module}")
|
|
strs+=( "$s" )
|
|
local -i sizeof_s=${#s}
|
|
(( sizeof_s > colsize )) && colsize=sizeof_s
|
|
n+=1
|
|
done
|
|
colsize+=2
|
|
|
|
local -i 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[@]}"; do
|
|
printf "${module}\n" 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_illegal_opt "$1"
|
|
;;
|
|
* )
|
|
args+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
if (( ${#args[@]} > 0 )); then
|
|
std::die 3 "%s %s: %s" \
|
|
"${CMD}" "${subcommand}" \
|
|
"no arguments allowed"
|
|
fi
|
|
|
|
"${output_function}"
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# clear
|
|
#
|
|
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 -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
|
|
std::die 3 "%s %s: %s" \
|
|
"${CMD}" "${subcommand}" \
|
|
"no arguments allowed"
|
|
fi
|
|
pmodules_init
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# search [switches] [STRING...]
|
|
#
|
|
Subcommands[search]='search'
|
|
Subcommands[find]='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 [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 modules=()
|
|
local groups=()
|
|
local with_modules='//'
|
|
local -ir cols=$(tput cols) # get number of columns of terminal
|
|
local -i max_len_modulename=0
|
|
local src_prefix=()
|
|
local opt_print_header='yes'
|
|
local opt_print_modulefiles='no'
|
|
local opt_print_csv='no'
|
|
local opt_print_verbose='no'
|
|
local opt_use_releases=':'
|
|
local opt_all_deps='no'
|
|
local opt_wrap='no'
|
|
local opt_glob='no'
|
|
local opt_newest='no'
|
|
|
|
#.....................................................................
|
|
#
|
|
# 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 (( ${#str} >= cols )); then
|
|
str="${str:0:$((cols-1))}>"
|
|
fi
|
|
std::info "${str}"
|
|
}
|
|
if [[ "${opt_wrap}" == 'no' ]]; then
|
|
local deps="${@:6}"
|
|
local str=$(printf "${fmt}" "$1" "$2" "$3" "$5" "${deps[@]}")
|
|
write_line "${str}"
|
|
else
|
|
local deps=( "${@:6}" )
|
|
local str=$(printf "${fmt}" "$1" "$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_csv() {
|
|
:
|
|
}
|
|
|
|
print_csv() {
|
|
fmt=''
|
|
func_print_header='print_header_none'
|
|
func_print_line='print_line_csv'
|
|
}
|
|
|
|
if [[ "${opt_print_modulefiles}" == 'yes' ]]; then
|
|
print_modulefiles
|
|
elif [[ "${opt_print_csv}" == 'yes' ]]; then
|
|
print_csv
|
|
elif [[ "${opt_print_verbose}" == 'yes' ]]; then
|
|
print_verbose
|
|
else
|
|
print_default
|
|
fi
|
|
|
|
local _script=''
|
|
if [[ ${opt_newest} == 'yes' ]]; then
|
|
_script='{} END{print}'
|
|
fi
|
|
${func_print_header}
|
|
while read -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
|
|
# Args:
|
|
# $1: module name pattern
|
|
#
|
|
# Variables used from enclosing function
|
|
# :FIXME:
|
|
#
|
|
search () {
|
|
local module="$1"
|
|
local group="$2"
|
|
# write results to a temporary file for later processing
|
|
|
|
# loop over all directories which can be added to
|
|
# MODULEPATH inside current group
|
|
local depth=${GroupDepths[${group}]}
|
|
local s=''
|
|
if (( depth > 0 )); then
|
|
s=$(printf '/*%.0s' $(seq 1 ${depth}))
|
|
fi
|
|
local modulepath=( ${src_prefix[@]/%//${group}/modulefiles$s} )
|
|
|
|
# get and print all available modules in $mpath
|
|
# with respect to the requested release stage
|
|
# tmpfile: module/version rel_stage group dependencies...
|
|
local mods
|
|
get_available_modules \
|
|
mods \
|
|
"${module}" \
|
|
"${opt_use_rel_stages}" \
|
|
"${modulepath[@]}"
|
|
local i=0
|
|
for (( i=0; i<${#mods[@]}; i+=4 )); do
|
|
local name=${mods[i]}
|
|
local rel_stage=${mods[i+1]}
|
|
local modulefile=${mods[i+2]}
|
|
local ol=${mods[i+3]}
|
|
|
|
if (( ${#name} > max_len_modulename)); then
|
|
max_len_modulename=${#name}
|
|
fi
|
|
|
|
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
|
|
deps=($(< "${dependencies_file}"))
|
|
else
|
|
deps=()
|
|
fi
|
|
else
|
|
# get dependencies encoded in directory name
|
|
local deps=()
|
|
local -i j
|
|
IFS='/' # note: IFS is used to concat in the for loop!
|
|
local toks=( ${modulefile} )
|
|
for ((j = -depth-2; j < -2; j += 2)); do
|
|
deps+=( "${toks[*]: $j:2}" );
|
|
done
|
|
unset IFS
|
|
fi
|
|
|
|
echo ${name} ${rel_stage} ${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-csv )
|
|
opt_print_csv='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}" || \
|
|
std::die 1 "%s %s: %s -- %s" \
|
|
"${CMD}" 'search' \
|
|
"illegal release stage" \
|
|
"${arg}"
|
|
opt_use_rel_stages+="${arg}:"
|
|
;;
|
|
--with | --with=* )
|
|
if [[ "$1" == --with ]]; then
|
|
local arg=$2
|
|
shift
|
|
else
|
|
local arg=${1/--with=}
|
|
fi
|
|
if [[ -z ${arg} ]] || [[ "${arg}" =~ "-*" ]]; then
|
|
std::die 1 "%s %s: %s -- %s" \
|
|
"${CMD}" 'search' \
|
|
"illegal value for --with option" \
|
|
"${arg}"
|
|
fi
|
|
arg=${arg//:/ }
|
|
arg=${arg//,/ }
|
|
for module in ${arg}; do
|
|
with_modules+=" && / ${module//\//\\/}/"
|
|
done
|
|
;;
|
|
-a | --all-releases-stages )
|
|
opt_use_rel_stages+="${ReleaseStages}"
|
|
;;
|
|
--src | --src=*)
|
|
if [[ "$1" == --src ]]; then
|
|
local dir="$2"
|
|
shift
|
|
else
|
|
local dir="${1/--src=}"
|
|
fi
|
|
if [[ ! -e "${dir}" ]]; then
|
|
std::die 1 "%s %s: %s -- %s" \
|
|
"${CMD}" 'search' \
|
|
"illegal value for --src option" \
|
|
"${dir} does not exist"
|
|
fi
|
|
if [[ ! -d "${dir}" ]]; then
|
|
std::die 1 "%s %s: %s -- %s" \
|
|
"${CMD}" 'search' \
|
|
"illegal value for --src option" \
|
|
"${dir} is not a directory"
|
|
fi
|
|
src_prefix+=( $(std::get_abspath "${src_prefix}") )
|
|
;;
|
|
-v | --verbose )
|
|
opt_print_verbose='yes'
|
|
;;
|
|
--wrap )
|
|
opt_wrap='yes'
|
|
;;
|
|
--glob )
|
|
opt_glob='yes'
|
|
;;
|
|
--newest )
|
|
opt_newest='yes'
|
|
;;
|
|
--group | --group=* )
|
|
if [[ $1 == *=* ]]; then
|
|
groups+=( ${1/--*=} )
|
|
else
|
|
groups+=( $2 )
|
|
shift
|
|
fi
|
|
;;
|
|
|
|
-- )
|
|
shift 1
|
|
modules+=( "$@" )
|
|
break
|
|
;;
|
|
* )
|
|
modules+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
if [[ -z "${src_prefix}" ]]; then
|
|
local ol=''
|
|
for ol in "${UsedOverlays[@]}"; do
|
|
src_prefix+=( "${OverlayInfo[${ol}:mod_root]}" )
|
|
done
|
|
fi
|
|
|
|
if [[ "${opt_use_rel_stages}" == ":" ]]; then
|
|
opt_use_rel_stages=":${UsedReleaseStages}:"
|
|
fi
|
|
|
|
if [[ ${#modules[@]} == 0 ]]; then
|
|
modules+=( '' )
|
|
fi
|
|
|
|
# :FIXME: do we need this?
|
|
if (( ${#GroupDepths[@]} == 0 )) || \
|
|
[[ ${src_prefix} != ${PMODULES_HOME%%/Tools*} ]]; then
|
|
scan_groups "${src_prefix}"
|
|
g_env_must_be_saved='yes'
|
|
fi
|
|
|
|
local module
|
|
if (( ${#groups[@]} == 0 )); then
|
|
groups=( "${!GroupDepths[@]}" )
|
|
fi
|
|
for module in "${modules[@]}"; do
|
|
if [[ ${opt_glob} != 'yes' ]]; then
|
|
module+="*"
|
|
fi
|
|
local group
|
|
for group in "${groups[@]}"; do
|
|
search "${module}" "${group}"
|
|
done
|
|
print_result
|
|
echo -n > ${tmpfile}
|
|
done
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# help [module|sub-command]
|
|
#
|
|
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
|
|
+ initadd modulefile [modulefile ...]
|
|
+ initprepend modulefile [modulefile ...]
|
|
+ initrm modulefile [modulefile ...]
|
|
+ initswitch modulefile1 modulefile2
|
|
+ initlist
|
|
+ initclear
|
|
'
|
|
|
|
subcommand_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 [[ -n "${Help[${arg}]}" ]] ; then
|
|
print_help "${arg}"
|
|
else
|
|
# :FIXME: print help of newest *available* module
|
|
# (respecting UsedReleaseStages)
|
|
"${modulecmd}" "${Shell}" "${subcommand}" "${arg}"
|
|
fi
|
|
done
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# whatis
|
|
#
|
|
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 options=()
|
|
local args=()
|
|
while (( $# > 0 )); do
|
|
case $1 in
|
|
-\? | --help )
|
|
print_help "${subcommand}"
|
|
;;
|
|
-a | --all )
|
|
options+=( '-a' )
|
|
;;
|
|
-- )
|
|
shift 1
|
|
args+=( "$@" )
|
|
break
|
|
;;
|
|
* )
|
|
args+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
local group=''
|
|
for group in "${!GroupDepths[@]}"; do
|
|
local mod_name=''
|
|
local file_name=''
|
|
while read mod_name file_name; do
|
|
[[ -n ${file_name} ]] || continue
|
|
local whatis=$("${modulecmd}" bash \
|
|
whatis \
|
|
"${file_name}" \
|
|
2>&1 1>/dev/null)
|
|
printf "%-25s: %s\n" "${mod_name}" "${whatis/*:}" 1>&2
|
|
done < <(set +x; subcommand_search \
|
|
--group "${group}" \
|
|
--print-modulefiles \
|
|
--newest \
|
|
"${options[@]}" \
|
|
"${args[@]}" 2>&1)
|
|
done
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# apropos
|
|
#
|
|
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 options=()
|
|
local args=()
|
|
while (( $# > 0 )); do
|
|
case $1 in
|
|
-\? | --help )
|
|
print_help "${subcommand}"
|
|
;;
|
|
-a | --all )
|
|
options+=( '-a' )
|
|
;;
|
|
-- )
|
|
shift 1
|
|
args+=( "$@" )
|
|
break
|
|
;;
|
|
* )
|
|
args+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
if (( ${#args[@]} == 0 )); then
|
|
std::die 3 "%s %s: %s" \
|
|
"${CMD}" "${subcommand}" \
|
|
"no search string specified"
|
|
elif (( ${#args[@]} > 1 )); then
|
|
std::die 3 "%s %s: %s" \
|
|
"${CMD}" "${subcommand}" \
|
|
"more then one search string specified"
|
|
fi
|
|
local arg="${args[0]}"
|
|
local group=''
|
|
for group in "${!GroupDepths[@]}"; do
|
|
local mod_name=''
|
|
local file_name=''
|
|
while read mod_name file_name; do
|
|
[[ -n ${file_name} ]] || continue
|
|
local whatis=$("${modulecmd}" bash \
|
|
whatis \
|
|
"${file_name}" \
|
|
2>&1 1>/dev/null)
|
|
if [[ ${whatis,,} =~ ${arg,,} ]]; then
|
|
printf "%-25s: %s\n" "${mod_name}" "${whatis/*:}" 1>&2
|
|
fi
|
|
done < <(set +x; subcommand_search \
|
|
--group "${group}" \
|
|
--print-modulefiles \
|
|
"${options[@]}" 2>&1)
|
|
done
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# save [collection]
|
|
#
|
|
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 COLLECTIONS_DIR="${HOME}/.Pmodules/collections"
|
|
subcommand_save() {
|
|
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_too_many_args
|
|
|
|
local collection=''
|
|
if (( ${#args[@]} == 0 )); then
|
|
collection="${COLLECTIONS_DIR}/default"
|
|
else
|
|
if [[ "${args[0]:0:1}" == '/' ]]; then
|
|
collection="${args[0]}"
|
|
else
|
|
[[ ${args[0]} == */* ]] && \
|
|
die_invalid_collection_name "${args[0]}"
|
|
collection="${COLLECTIONS_DIR}/${args[0]}"
|
|
fi
|
|
fi
|
|
mkdir -p "${collection%/*}" || \
|
|
die_cannot create_directory "$_"
|
|
|
|
# save used release stages, groups and overlays
|
|
local -- item=''
|
|
local -a items=()
|
|
local -a tmp=()
|
|
|
|
IFS=':' read -a tmp <<< "${UsedReleaseStages}"
|
|
items+=( "${tmp[@]}")
|
|
IFS=':' read -a tmp <<< "${UsedGroups}"
|
|
items+=( "${tmp[@]}")
|
|
IFS=':' read -a tmp <<< "${UsedOverlays}"
|
|
items+=( "${tmp[@]}")
|
|
for item in "${items[@]}"; do
|
|
s+="module use ${item};\n"
|
|
done
|
|
|
|
# save additional module directories
|
|
local -a modulepath=()
|
|
IFS=':' read -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 -a modules <<< "${LOADEDMODULES}"
|
|
local -- m=''
|
|
for m in "${modules[@]}"; do
|
|
s+="module load $m;\n"
|
|
done
|
|
|
|
# save collection
|
|
echo -e "$s" > "${collection}" || \
|
|
die_cannot_save_collection "${collection}"
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# restore [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.
|
|
|
|
"
|
|
subcommand_restore() {
|
|
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_too_many_args
|
|
|
|
local collection=''
|
|
if (( ${#args[@]} == 0 )); then
|
|
collection="${COLLECTIONS_DIR}/default"
|
|
else
|
|
if [[ "${args[0]:0:1}" == '/' ]]; then
|
|
collection="${args[0]}"
|
|
else
|
|
[[ ${args[0]} == */* ]] && \
|
|
die_invalid_collection_name "${args[0]}"
|
|
collection="${COLLECTIONS_DIR}/${args[0]}"
|
|
fi
|
|
fi
|
|
[[ -r "${collection}" ]] || \
|
|
die_collection_doesnt_exist "${collection}"
|
|
IFS=':' read -a modules <<< "${LOADEDMODULES}"
|
|
local -- m=''
|
|
for ((i=${#modules[@]}-1; i>=0; i--)); do
|
|
subcommand_unload "${modules[$i]}"
|
|
done
|
|
|
|
local -a items=()
|
|
local -a tmp=()
|
|
|
|
IFS=':' read -a tmp <<< "${UsedReleaseStages}"
|
|
items+=( "${tmp[@]}")
|
|
IFS=':' read -a tmp <<< "${UsedGroups}"
|
|
items+=( "${tmp[@]}")
|
|
IFS=':' read -a tmp <<< "${UsedOverlays}"
|
|
items+=( "${tmp[@]}")
|
|
|
|
local -- item=''
|
|
for item in "${items[@]}"; do
|
|
subcommand_unuse "${item}"
|
|
done
|
|
save_env
|
|
export_env PMODULES_ENV
|
|
cat "${collection}"
|
|
g_env_must_be_saved='no'
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# module savelist [pattern...]
|
|
#
|
|
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 -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 -- pattern=''
|
|
local -a collections=()
|
|
for pattern in "${args[@]}"; do
|
|
collections+=( ${COLLECTIONS_DIR}/* )
|
|
done
|
|
echo "Saved collections:" 1>&2
|
|
local collection=''
|
|
for collection in "${collections[@]}"; do
|
|
[[ -r "${collection}" ]] || continue
|
|
echo -e "\t${collection/${COLLECTIONS_DIR}\/}" 1>&2
|
|
done
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# module saverm [collection...]
|
|
#
|
|
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 -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
|
|
for collection in "${args[@]}"; do
|
|
test -e "${COLLECTIONS_DIR}/${collection}" || \
|
|
die_collection_doesnt_exist "${collection}"
|
|
rm -f "${COLLECTIONS_DIR}/${collection}"
|
|
done
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# module saveshow [collection]
|
|
#
|
|
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 -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
|
|
for collection in "${args[@]}"; do
|
|
[[ -e "${COLLECTIONS_DIR}/${collection}" ]] || \
|
|
die_collection_doesnt_exist "${collection}"
|
|
echo "Collection '${collection}':" 1>&2
|
|
cat "${COLLECTIONS_DIR}/${collection}" 1>&2
|
|
done
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# initadd module...
|
|
#
|
|
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() {
|
|
subcommand_generic1plus "$@"
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# initprepend module...
|
|
#
|
|
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() {
|
|
subcommand_generic1plus "$@"
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# initrm module...
|
|
#
|
|
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() {
|
|
subcommand_generic1plus "$@"
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# initswitch module1 module2
|
|
#
|
|
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 args=()
|
|
while (( $# > 0 )); do
|
|
case $1 in
|
|
-\? | --help )
|
|
print_help "${subcommand}"
|
|
;;
|
|
-- )
|
|
shift 1
|
|
args+=( "$@" )
|
|
break
|
|
;;
|
|
* )
|
|
args+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
if (( ${#args[@]} != 2 )); then
|
|
std::die 3 "%s %s: %s" \
|
|
"${CMD}" "${subcommand}" \
|
|
"two arguments required not less not more"
|
|
fi
|
|
"${modulecmd}" "${Shell}" "${subcommand}" "${args[@]}"
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# initlist
|
|
#
|
|
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() {
|
|
subcommand_generic0 "$@"
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# initclear
|
|
#
|
|
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() {
|
|
subcommand_generic0 "$@"
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# main
|
|
#
|
|
|
|
# parse arguments
|
|
#
|
|
|
|
# first argument must be a shell!
|
|
case "$1" in
|
|
sh | bash | zsh )
|
|
declare Shell="sh"
|
|
;;
|
|
csh | tcsh )
|
|
declare Shell='csh'
|
|
;;
|
|
python )
|
|
declare Shell='python'
|
|
;;
|
|
* )
|
|
std::die 1 "${CMD}: unsupported shell -- $1"
|
|
;;
|
|
esac
|
|
shift
|
|
|
|
# parse agruments till and including the sub-command
|
|
declare -a opts=()
|
|
while (( $# > 0 )); do
|
|
case $1 in
|
|
-\? | -H | --help | -help )
|
|
print_help 'help'
|
|
;;
|
|
-V | --version )
|
|
print_help 'version'
|
|
;;
|
|
--debug )
|
|
set -x
|
|
;;
|
|
'' )
|
|
;;
|
|
-* )
|
|
opts+=( "$1" )
|
|
;;
|
|
* )
|
|
subcommand="$1"
|
|
shift
|
|
break
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
if [[ -z "${subcommand}" ]]; then
|
|
std::die 1 "${CMD}: no sub-command specified."
|
|
fi
|
|
|
|
if [[ -z "${Subcommands[${subcommand}]}" ]]; then
|
|
std::die 1 "${CMD}: unknown sub-command -- ${subcommand}"
|
|
fi
|
|
|
|
# restore variables from last call
|
|
unset Version
|
|
if [[ -n ${PMODULES_ENV} ]]; then
|
|
eval "$("${base64}" -d <<< "${PMODULES_ENV}" 2>/dev/null)"
|
|
fi
|
|
# Version should now be defined again, if not:
|
|
# - PMODULES_ENV was not set
|
|
# - Version was not defined the last time the status was saved.
|
|
# This is true for older Pmodules versions.
|
|
|
|
# We (re-)initialise the Pmodules system, if
|
|
# - PMODULES_ENV was not set/is empty
|
|
# - A new Pmodules version has been loaded
|
|
if [[ -z ${Version} ]] || [[ ${Version} != ${PMODULES_VERSION} ]]; then
|
|
declare _tmp_loaded_modules_="${LOADEDMODULES}"
|
|
declare _tmp_lmfiles_="${_LMFILES_}"
|
|
|
|
pmodules_init
|
|
|
|
LOADEDMODULES="${_tmp_loaded_modules_}"
|
|
_LMFILES_="${_tmp_lmfiles_}"
|
|
export_env \
|
|
LOADEDMODULES \
|
|
_LMFILES_
|
|
fi
|
|
|
|
# we need to handle help text and options for sub-cmd aliases
|
|
subcommand=${Subcommands[${subcommand}]}
|
|
|
|
# We need a tmp-file in the following sub-commands. It will be removed
|
|
# in the exit function if exists.
|
|
case ${subcommand} in
|
|
load|purge|search|swap|whatis|apropos )
|
|
declare -r tmpfile=$( ${mktemp} /tmp/Pmodules.XXXXXX ) \
|
|
|| std::die 1 "Oops: unable to create tmp file!"
|
|
;;
|
|
* )
|
|
declare -r tmpfile=''
|
|
;;
|
|
esac
|
|
|
|
# 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_${Subcommands[$subcommand]} "$@"
|
|
|
|
# Local Variables:
|
|
# mode: sh
|
|
# sh-basic-offset: 8
|
|
# tab-width: 8
|
|
# End:
|