mirror of
https://github.com/Pmodules/Pmodules.git
synced 2026-06-23 16:17:59 +02:00
2035 lines
53 KiB
Bash
Executable File
2035 lines
53 KiB
Bash
Executable File
#!@PMODULES_HOME@/sbin/bash --noprofile
|
|
#
|
|
|
|
#set -o nounset
|
|
# we have to unset CDPATH, otherwise 'cd' prints the directoy!
|
|
unset CDPATH
|
|
|
|
# used for some output only
|
|
declare -r CMD='module'
|
|
|
|
declare -r mydir=$(cd $(dirname "$0") && pwd)
|
|
declare prefix=$(dirname "${mydir}")
|
|
declare -r sbindir="${prefix}/sbin"
|
|
declare -r libdir="${prefix}/lib"
|
|
declare -r libexecdir="${prefix}/libexec"
|
|
|
|
declare -r base64="${sbindir}/base64"
|
|
declare -r mktemp="${sbindir}/mktemp"
|
|
declare -r sort="${sbindir}/sort"
|
|
declare -r getopt="${sbindir}/getopt"
|
|
|
|
source "${libdir}/libstd.bash"
|
|
source "${libdir}/libpmodules.bash"
|
|
|
|
: ${PMODULES_DEFINED_RELEASES:=':unstable:stable:deprecated:'}
|
|
|
|
declare -r version='@PMODULES_VERSION@'
|
|
|
|
if [[ ${PMODULES_PURETCL} == yes ]]; then
|
|
declare -r modulecmd="${libexecdir}/modulecmd.tcl"
|
|
else
|
|
declare -rx TCLLIBPATH="${PMODULES_HOME}/lib/Pmodules"
|
|
declare -r modulecmd="${libexecdir}/modulecmd.bin"
|
|
fi
|
|
|
|
declare verbosity_lvl=${PMODULES_VERBOSITY:-'verbose'}
|
|
|
|
shopt -s nullglob
|
|
|
|
declare -A GroupDepths='()'
|
|
declare Shell=''
|
|
declare -A Subcommands
|
|
declare -A Options
|
|
declare -A Help
|
|
|
|
Help['version']="
|
|
Pmodules ${version} using Tcl Environment Modules @MODULES_VERSION@
|
|
Copyright GNU GPL v2
|
|
"
|
|
|
|
print_help() {
|
|
echo -e "${Help[$1]}" 1>&2
|
|
std::die 1
|
|
}
|
|
|
|
export_env() {
|
|
case "${Shell}" in
|
|
sh | bash | zsh )
|
|
local -r fmt="export %s=\"%s\"; "
|
|
;;
|
|
csh | tcsh )
|
|
local -r fmt="setenv %s \"%s\"; "
|
|
;;
|
|
* )
|
|
std::die 1 "Unsupported shell -- ${Shell}\n"
|
|
;;
|
|
esac
|
|
|
|
while (( $# > 0 )); do
|
|
printf "${fmt}" "$1" "${!1}"
|
|
shift
|
|
done
|
|
}
|
|
|
|
#
|
|
# Save/cache some variables.
|
|
# This function is called on exit via a trap handler.
|
|
#
|
|
# Args;
|
|
# none
|
|
#
|
|
declare g_env_must_be_saved='no'
|
|
|
|
save_env() {
|
|
[[ ${g_env_must_be_saved} == 'no' ]] && return 0
|
|
local vars=( GroupDepths UsedReleases UseFlags UsedGroups )
|
|
vars+=( PMODULES_DEFAULT_GROUPS PMODULES_DEFINED_RELEASES )
|
|
vars+=( PMODULES_DEFAULT_RELEASES )
|
|
|
|
local s=$(typeset -p ${vars[@]})
|
|
declare -g PMODULES_ENV=$( "${base64}" --wrap=0 <<< "$s" )
|
|
export_env 'PMODULES_ENV'
|
|
}
|
|
|
|
trap 'save_env ' EXIT
|
|
|
|
#
|
|
# get release of module
|
|
# Note:
|
|
# - the release of a modulefile outside ${PMODULES_ROOT} is 'stable'
|
|
# - the release of a modulefile inside ${PMODULES_ROOT} without a
|
|
# coresponding release file is 'unstable'
|
|
#
|
|
# Args:
|
|
# $1: absolute modulefile name
|
|
#
|
|
get_release() {
|
|
local "$1"
|
|
local -r modulefile="$2"
|
|
|
|
# is modulefile outside ${PMODULES_ROOT}?
|
|
if [[ ! ${modulefile} =~ ${PMODULES_ROOT} ]]; then
|
|
std::upvar $1 'stable'
|
|
return 0
|
|
fi
|
|
|
|
# we are inside ${PMODULES_ROOT}
|
|
local -r releasefile="${modulefile%/*}/.release-${modulefile##*/}"
|
|
if [[ -r ${releasefile} ]]; then
|
|
# read releasefile, remove empty lines, spaces etc
|
|
local -r data=$( < "${releasefile}" )
|
|
std::upvar $1 "${data}"
|
|
else
|
|
std::upvar $1 'unstable'
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
is_release() {
|
|
[[ ${PMODULES_DEFINED_RELEASES} =~ :$1: ]]
|
|
}
|
|
|
|
#
|
|
# Check whether argument is a group
|
|
#
|
|
# Args:
|
|
# $1: string
|
|
#
|
|
is_group () {
|
|
local -r group="$1"
|
|
# arg isn't emtpy and group already in cache
|
|
[[ -n ${group} ]] && [[ -n ${GroupDepths[${group}]} ]] && return 0
|
|
local moduledir="${PMODULES_ROOT}/${group}/${PMODULES_MODULEFILES_DIR}"
|
|
[[ -d "${moduledir}" ]] || return 1
|
|
compute_group_depth "${moduledir}"
|
|
}
|
|
|
|
#
|
|
# check shebang
|
|
# $1: file name to test
|
|
is_modulefile() {
|
|
local -r fname="$1"
|
|
local shebang
|
|
[[ -r ${fname} ]] || return 1
|
|
read -n 11 shebang < "${fname}"
|
|
[[ "${shebang:0:8}" == '#%Module' ]] || [[ "${shebang:0:9}" == '#%Pmodule' ]]
|
|
}
|
|
|
|
subcommand_generic0() {
|
|
local -r subcommand="$1"
|
|
shift
|
|
local -a args=()
|
|
while (( $# > 0 )); do
|
|
case $1 in
|
|
-H | --help )
|
|
print_help "${subcommand}"
|
|
;;
|
|
-- )
|
|
:
|
|
;;
|
|
* )
|
|
args+=( "$1" )
|
|
;;
|
|
esac
|
|
done
|
|
if (( ${#args[@]} > 0 )); then
|
|
std::die 3 "%s %s: %s\n" \
|
|
"${CMD}" "${subcommand}" \
|
|
"no arguments allowed"
|
|
fi
|
|
"${modulecmd}" "${Shell}" "${subcommand}"
|
|
}
|
|
|
|
subcommand_generic1() {
|
|
local -r subcommand="$1"
|
|
shift
|
|
local -a args=()
|
|
while (( $# > 0 )); do
|
|
case $1 in
|
|
-H | --help )
|
|
print_help "${subcommand}"
|
|
;;
|
|
-- )
|
|
:
|
|
;;
|
|
* )
|
|
args+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
if (( ${#args[@]} == 0 )); then
|
|
std::die 3 "%s %s: %s\n" \
|
|
"${CMD}" "${subcommand}" \
|
|
"missing argument"
|
|
elif (( ${#args[@]} > 1 )); then
|
|
std::die 3 "%s %s: %s\n" \
|
|
"${CMD}" "${subcommand}" \
|
|
"only one argument allowed"
|
|
fi
|
|
"${modulecmd}" "${Shell}" "${subcommand}" "${args[@]}"
|
|
}
|
|
|
|
subcommand_generic1plus() {
|
|
local -r subcommand="$1"
|
|
shift
|
|
local args=()
|
|
while (( $# > 0 )); do
|
|
case $1 in
|
|
-H | --help )
|
|
print_help "${subcommand}"
|
|
;;
|
|
-- )
|
|
:
|
|
;;
|
|
* )
|
|
args+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
if (( ${#args[@]} == 0 )); then
|
|
std::die 3 "%s %s: %s\n" \
|
|
"${CMD}" "${subcommand}" \
|
|
"missing argument"
|
|
fi
|
|
"${modulecmd}" "${Shell}" "${subcommand}" "${args[@]}"
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# load [-fsvw] <module>
|
|
#
|
|
# $1: module to load
|
|
#
|
|
Subcommands[add]='load'
|
|
Subcommands[load]='load'
|
|
Options['load']='-l help -o Hfsvwi -l force -l silent -l verbose -l warn -l internal'
|
|
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 subcommand='load'
|
|
local release='undef'
|
|
local current_modulefile=''
|
|
local prefix=''
|
|
local m=''
|
|
|
|
local saved_IFS="${IFS}";
|
|
IFS=':'
|
|
local -a modulepath=(${MODULEPATH})
|
|
IFS=${saved_IFS}
|
|
|
|
#
|
|
# Test whether a given module is available.
|
|
# The passed module-name can be
|
|
#
|
|
# - an absolute file- or link-name.
|
|
# The module can be either in- or outside our hierarchy.
|
|
#
|
|
# - a relative file- or link-name.
|
|
# The module can be either in- or outside out hierarchy.
|
|
#
|
|
# - specified with name and version (like gcc/5.2.0).
|
|
# The module can be either in- or outside our hierarchy.
|
|
#
|
|
# - specified with name only (without version, like gcc).
|
|
# The module can be either in- or outside our hierarchy.
|
|
#
|
|
# - directory in- or outsite our hierarchy (not supported by
|
|
# modulecmd.tcl!)
|
|
#
|
|
# arguments:
|
|
# $1: module name or file
|
|
#
|
|
# possible return values:
|
|
# 0: module is loadable
|
|
# 1: either not a modulefile or unsused release
|
|
#
|
|
# The following variables in the enclosing function are set:
|
|
# current_modulefile
|
|
# prefix
|
|
# release
|
|
#
|
|
is_available() {
|
|
local m=$1
|
|
local -a array
|
|
#
|
|
# the next command assigns the absolute modulefile path
|
|
# to ${arry[0]} and - if the module is in our root - the
|
|
# prefix of the module to ${array[1]}.
|
|
#
|
|
# The trick with the first line matching "_PREFIX" is not
|
|
# 100% reliable: One of the Pmodules extensions must be
|
|
# called before something like
|
|
# setenv FOO_PREFIX bar
|
|
# can be used.
|
|
#
|
|
mapfile -t array < <("${modulecmd}" 'bash' show "$m" 2>&1 | \
|
|
awk 'NR == 2 {print substr($0, 1, length($0)-1)}; /_PREFIX |_HOME / {print $3; exit}')
|
|
current_modulefile="${array[0]}"
|
|
prefix="${array[1]}"
|
|
test -n "${current_modulefile}" || return 1
|
|
get_release release "${current_modulefile}" "${UsedReleases}"
|
|
}
|
|
|
|
#
|
|
# output load 'hints'
|
|
#
|
|
# Note:
|
|
# The variable 'm' from the parent function will be used
|
|
# but not changed.
|
|
#
|
|
# Args:
|
|
# none
|
|
output_load_hints() {
|
|
local output=''
|
|
local release=''
|
|
while read -a line; do
|
|
release=${line[1]}
|
|
if [[ ! ":${UsedReleases}:" =~ "${release}" ]]; then
|
|
output+="module use ${release}; "
|
|
fi
|
|
local group=${line[2]}
|
|
if [[ ! ":${UsedGroups}:" =~ ":${group}:" ]] && \
|
|
(( ${GroupDepths[${group}]} == 0 )); then
|
|
output+="module use ${group}; "
|
|
fi
|
|
output+="module load ${line[@]:3} ${line[0]}\n"
|
|
done < <(subcommand_search "${m}" -a --no-header 2>&1)
|
|
if [[ -n "${output}" ]]; then
|
|
std::info "\nTry with one of the following command(s):\n"
|
|
std::die 3 "${output}\n"
|
|
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}"
|
|
}
|
|
|
|
local args=()
|
|
opts=()
|
|
while (($# > 0)); do
|
|
case $1 in
|
|
-H | --help )
|
|
print_help "${subcommand_load}"
|
|
;;
|
|
-f | --force )
|
|
opts+=(' -f')
|
|
;;
|
|
-s | --silent )
|
|
verbosity_lvl='silent'
|
|
;;
|
|
-v | --verbose )
|
|
verbosity_lvl='verbose'
|
|
;;
|
|
-w | --warn )
|
|
verbosity_lvl='warn'
|
|
;;
|
|
-- )
|
|
;;
|
|
* )
|
|
args+=( $1 )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
if (( ${#args[@]} == 0 )); then
|
|
std::die 2 "%s %s: %s\n" \
|
|
"${CMD}" "${subcommand}" \
|
|
"No module specified"
|
|
fi
|
|
for m in "${args[@]}"; do
|
|
if [[ "$m" =~ ":" ]]; then
|
|
|
|
# extendet module name is either
|
|
# - group:name or
|
|
# - group:name:release or
|
|
# - release:name or
|
|
# - release:group:name or
|
|
# - name:release
|
|
|
|
local save_ifs=${IFS}
|
|
IFS=':'
|
|
local -a toks=($m)
|
|
IFS=${save_ifs}
|
|
local group=''
|
|
local release=''
|
|
if is_group "${toks[0]}"; then
|
|
group=${toks[0]}
|
|
m=${toks[1]}
|
|
release=${toks[2]}
|
|
elif is_release "${toks[0]}"; then
|
|
release=${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]}
|
|
release=${toks[2]}
|
|
else
|
|
release=${toks[1]}
|
|
group=${toks[2]}
|
|
fi
|
|
fi
|
|
if [[ -n ${group} ]]; then
|
|
is_group "${group}" || \
|
|
std::die 3 "%s %s: %s -- %s\n" \
|
|
"${CMD}" "${subcommand}" \
|
|
"illegal group name" \
|
|
"${group}"
|
|
local -i depth=${GroupDepths[${group}]}
|
|
(( depth != 0 )) && \
|
|
std::die 3 "%s %s: %s -- %s\n" \
|
|
"${CMD}" "${subcommand}" \
|
|
"illegal group name" \
|
|
"${group}"
|
|
MODULEPATH="${PMODULES_ROOT}/${group}/"
|
|
MODULEPATH+="${PMODULES_MODULEFILES_DIR}"
|
|
modulepath=( ${MODULEPATH} )
|
|
fi
|
|
if [[ -n ${release} ]]; then
|
|
is_release "${release}" || \
|
|
std::die 3 "%s %s: %s -- %s\n" \
|
|
"${CMD}" "${subcommand}" \
|
|
"illegal release name"
|
|
"${release}"
|
|
std::append_path UsedReleases "${release}"
|
|
g_env_must_be_saved='yes'
|
|
fi
|
|
fi
|
|
local found=''
|
|
for flag in "${UseFlags[@]/#/_}" ""; do
|
|
# :FIXME: this doesn't work if ${m} is a
|
|
# modulename without version
|
|
if is_available "${m}${flag}"; then
|
|
m+="${flag}"
|
|
found=':'
|
|
break
|
|
fi
|
|
done
|
|
if [[ ! "${found}" ]]; then
|
|
std::info "%s %s: module unavailable -- %s\n" \
|
|
"${CMD}" 'load' "${m}"
|
|
[[ ${verbosity_lvl} == 'verbose' ]] && output_load_hints
|
|
std::die 3 ""
|
|
fi
|
|
if [[ ${current_modulefile} =~ ${PMODULES_ROOT} ]] \
|
|
&& [[ ! ${m} =~ / ]]; then
|
|
m+="/${current_modulefile##*/}"
|
|
fi
|
|
if [[ ":${LOADEDMODULES}:" =~ ":${m}:" ]]; then
|
|
continue
|
|
fi
|
|
if [[ ${current_modulefile} =~ ${PMODULES_ROOT} ]]; then
|
|
# modulefile is in our hierarchy
|
|
# ${prefix} was set in is_available()!
|
|
test -r "${prefix}/.info" && cat "$_" 1>&2
|
|
test -r "${prefix}/.dependencies" && load_dependencies "$_"
|
|
fi
|
|
local tmpfile=$( "${mktemp}" /tmp/Pmodules.XXXXXX ) \
|
|
|| std::die 1 "Oops: unable to create tmp file!\n"
|
|
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'*}
|
|
local error_txt='failed'
|
|
if [[ "$s" =~ ' conflicts ' ]]; then
|
|
error_txt='conflicts with already loaded modules'
|
|
fi
|
|
std::die 3 "%s %s: %s -- %s\n" \
|
|
"${CMD}" "${subcommand}" \
|
|
"${error_txt}" \
|
|
"${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=$(printf "%s %s: %s -- %s" \
|
|
"${CMD}" 'load' \
|
|
"${release} module has been loaded" \
|
|
"${m}")
|
|
if [[ ${verbosity_lvl} != silent ]] && \
|
|
[[ ${release} != stable ]]; then
|
|
std::info "%s\n" "${msg}"
|
|
fi
|
|
logger "${msg}"
|
|
done
|
|
# fix LOADEDMODULES
|
|
LOADEDMODULES="${_LMFILES_}"
|
|
local dir
|
|
while read dir; do
|
|
[[ "${dir: -1}" == "/" ]] || dir+="/"
|
|
LOADEDMODULES="${LOADEDMODULES//${dir}}"
|
|
done <<< "${MODULEPATH//:/$'\n'}"
|
|
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() {
|
|
local -r 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}"
|
|
;;
|
|
-- )
|
|
;;
|
|
* )
|
|
args+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
if (( ${#args[@]} == 0 )); then
|
|
std::die 3 "%s %s: %s\n" \
|
|
"${CMD}" "${subcommand}" \
|
|
"missing argument"
|
|
fi
|
|
|
|
local arg
|
|
for arg in "${args[@]}"; do
|
|
local output=$("${modulecmd}" "${Shell}" 'unload' "${arg}")
|
|
eval "${output}"
|
|
case ${Shell} in
|
|
sh | bash | zsh )
|
|
echo "${output}"
|
|
;;
|
|
* )
|
|
"${modulecmd}" "${Shell}" 'unload' "${arg}"
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# 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 root name as modulefile2.
|
|
"
|
|
|
|
subcommand_swap() {
|
|
local -r subcommand='swap'
|
|
local args=()
|
|
while (( $# > 0 )); do
|
|
case $1 in
|
|
-H | --help )
|
|
print_help "${subcommand}"
|
|
;;
|
|
-- )
|
|
;;
|
|
* )
|
|
args+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
if (( ${#args[@]} == 0 )); then
|
|
std::die 3 "%s %s: %s\n" \
|
|
"${CMD}" "${subcommand}" \
|
|
"missing argument"
|
|
elif (( ${#args[@]} > 2 )); then
|
|
std::die 3 "%s %s: %s\n" \
|
|
"${CMD}" "${subcommand}" \
|
|
"too many arguments"
|
|
fi
|
|
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 -r subcommand='show'
|
|
local args=()
|
|
while (( $# > 0 )); do
|
|
case $1 in
|
|
-H | --help )
|
|
print_help "${subcommand}"
|
|
;;
|
|
-- )
|
|
;;
|
|
* )
|
|
args+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
if (( ${#args[@]} == 0 )); then
|
|
std::die 3 "%s %s: %s\n" \
|
|
"${CMD}" "${subcommand}" \
|
|
"missing argument"
|
|
fi
|
|
|
|
local arg
|
|
for arg in "${args[@]}"; do
|
|
"${modulecmd}" "${Shell}" "${subcommand}" "${arg}"
|
|
done
|
|
}
|
|
|
|
#
|
|
# get all available modules in given directory.
|
|
# return list like
|
|
# modulename1 release1 modulename2 release2 ...
|
|
#
|
|
get_available_modules() {
|
|
local -r dir="$1"
|
|
local -r module="$2"
|
|
local -r use_releases="${3:-${UsedReleases}}"
|
|
local -a mods=()
|
|
local release
|
|
test -d "${dir}" || return 0
|
|
{
|
|
cd "${dir}"
|
|
while read mod; do
|
|
get_release release "${dir}/${mod}"
|
|
|
|
if [[ :${use_releases}: =~ :${release}: ]]; then
|
|
mods+=( "${mod}" ${release} )
|
|
fi
|
|
done < <(find * \( -type f -o -type l \) -not -name ".*" -ipath "${module}*")
|
|
}
|
|
echo "${mods[@]}"
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# avail [-hlt] [<module-pattern>...]
|
|
#
|
|
Subcommands[avail]='avail'
|
|
Options[avail]='-l help -o Hahlmt -l all -l all-releases -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-releases
|
|
List all available modules independend of the release.
|
|
|
|
-t|--terse
|
|
Output in short format.
|
|
|
|
-l|--long
|
|
Output in long format.
|
|
|
|
-h|--human
|
|
Output in human readable format.
|
|
-m|--machine
|
|
Output in machine readable format
|
|
"
|
|
|
|
subcommand_avail() {
|
|
local -r 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=${dir/${PMODULES_ROOT}\/}
|
|
local caption=${caption/\/${PMODULES_MODULEFILES_DIR}/: }
|
|
local caption=${caption/: \//: }
|
|
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
|
|
for (( i=0; i<${#mods[@]}; i+=2 )); do
|
|
local mod=${mods[i]}
|
|
local release=${mods[i+1]}
|
|
case $release in
|
|
stable )
|
|
out=''
|
|
;;
|
|
* )
|
|
out="${release}"
|
|
;;
|
|
esac
|
|
printf "%-20s\t%s\n" "${mod}" "${out}" 1>&2
|
|
done
|
|
std::info "\n"
|
|
}
|
|
|
|
machine_output() {
|
|
for (( i=0; i<${#mods[@]}; i+=2 )); 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
|
|
for (( i=0; i<${#mods[@]}; i+=2 )); do
|
|
local mod=${mods[i]}
|
|
local release=${mods[i+1]}
|
|
case $release in
|
|
stable )
|
|
out=''
|
|
;;
|
|
* )
|
|
out=${release}
|
|
;;
|
|
esac
|
|
printf "%-20s\t%s\n" "${mod}" "${out}" 1>&2
|
|
done
|
|
std::info "\n"
|
|
}
|
|
|
|
human_readable_output() {
|
|
output_header
|
|
|
|
local -i column=$cols
|
|
local -i colsize=16
|
|
for ((i=0; i<${#mods[@]}; i+=2)); do
|
|
if [[ ${verbosity_lvl} == 'verbose' ]]; then
|
|
local release=${mods[i+1]}
|
|
case ${mods[i+1]} in
|
|
stable )
|
|
mod=${mods[i]}
|
|
;;
|
|
* )
|
|
mod="${mods[i]}(${release:0:1})"
|
|
;;
|
|
esac
|
|
else
|
|
mod=${mods[i]}
|
|
fi
|
|
local -i len=${#mod}
|
|
local -i span=$(( len / 16 + 1 ))
|
|
local -i colsize=$(( span * 16 ))
|
|
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_all_groups='no'
|
|
local opt_use_releases="${UsedReleases}"
|
|
while (($# > 0)); do
|
|
case $1 in
|
|
-H | --help )
|
|
print_help "${subcommand}"
|
|
;;
|
|
-a | --all )
|
|
opt_all_groups='yes'
|
|
opt_use_releases="${PMODULES_DEFINED_RELEASES}"
|
|
;;
|
|
--all-releases )
|
|
opt_use_releases="${PMODULES_DEFINED_RELEASES}"
|
|
;;
|
|
-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'
|
|
;;
|
|
-- | '' )
|
|
;;
|
|
* )
|
|
pattern+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
if (( ${#pattern[@]} == 0 )); then
|
|
pattern+=( '' )
|
|
fi
|
|
local saved_IFS=${IFS};
|
|
IFS=':'
|
|
local -a modulepath=(${MODULEPATH})
|
|
IFS=${saved_IFS}
|
|
local string
|
|
for string in "${pattern[@]}"; do
|
|
for dir in "${modulepath[@]}"; do
|
|
mods=( $( get_available_modules \
|
|
"${dir}" "${string}" \
|
|
"${opt_use_releases}" ) )
|
|
[[ ${#mods[@]} == 0 ]] && continue
|
|
${output_function}
|
|
done
|
|
done
|
|
}
|
|
|
|
#
|
|
# compute depth of modulefile directory.
|
|
#
|
|
# Args:
|
|
# $1: absolute path of a modulefile directory
|
|
#
|
|
compute_group_depth () {
|
|
local -r dir=$1
|
|
test -d "${dir}" || return 1
|
|
local group=${dir%/*}
|
|
local group=${group##*/}
|
|
[[ -n "${GroupDepths[${group}]}" ]] && return 0
|
|
local -i depth=$(find "${dir}" -depth \( -type f -o -type l \) \
|
|
-printf "%d" -quit 2>/dev/null)
|
|
(( depth-=2 ))
|
|
# if a group doesn't contain a modulefile, depth is negativ
|
|
# :FIXME: better solution?
|
|
(( depth < 0 )) && (( depth = 0 ))
|
|
GroupDepths[$group]=${depth}
|
|
g_env_must_be_saved='yes'
|
|
}
|
|
|
|
#
|
|
# (Re-)Scan available groups in given root and compute group depth's
|
|
#
|
|
# Args:
|
|
# $1: root of modulefile hierarchy
|
|
#
|
|
scan_groups () {
|
|
local -r root="$1"
|
|
local moduledir
|
|
for moduledir in ${root}/*/${PMODULES_MODULEFILES_DIR}; do
|
|
compute_group_depth "${moduledir}"
|
|
done
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# use [-a|--append|-p|--prepend] [directory|group|release...]
|
|
#
|
|
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...]
|
|
Without arguments this sub-command displays information about
|
|
the module search path, used families and releases. You can
|
|
use this sub-command to get a list of available families and
|
|
releases.
|
|
|
|
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() {
|
|
local -r subcommand='use'
|
|
local saved_IFS=${IFS};
|
|
IFS=':'
|
|
local -a modulepath=(${MODULEPATH})
|
|
IFS=${saved_IFS}
|
|
local add2path_func='std::append_path'
|
|
|
|
group_is_used() {
|
|
[[ :${UsedGroups}: =~ :$1: ]]
|
|
}
|
|
|
|
release_is_used() {
|
|
[[ ":${UsedReleases}:" =~ :$1: ]]
|
|
}
|
|
|
|
print_info() {
|
|
local f
|
|
local r
|
|
std::info "Used groups:\n"
|
|
for f in ${UsedGroups//:/ }; do
|
|
std::info "\t${f}\n"
|
|
done
|
|
std::info "\nUnused groups:\n"
|
|
local _group
|
|
for _group in "${!GroupDepths[@]}"; do
|
|
local -i depth=${GroupDepths[${_group}]}
|
|
if ! group_is_used "${_group}" && (( depth == 0 )); then
|
|
std::info "\t${_group}\n"
|
|
fi
|
|
done
|
|
|
|
std::info "\nUsed releases:\n"
|
|
for r in ${UsedReleases//:/ }; do
|
|
std::info "\t${r}\n"
|
|
done
|
|
std::info "\nUnused releases:\n"
|
|
for r in ${PMODULES_DEFINED_RELEASES//:/ }; do
|
|
if ! release_is_used $r; then
|
|
std::info "\t${r}\n"
|
|
fi
|
|
done
|
|
|
|
std::info "\nUsed flags:\n"
|
|
for flag in "${UseFlags//:/ }"; do
|
|
std::info "\t${flag}\n"
|
|
done
|
|
|
|
std::info "\nAdditonal directories in MODULEPATH:\n"
|
|
let n=0
|
|
for (( i=0; i<${#modulepath[@]}; i++)); do
|
|
if [[ ! ${modulepath[i]} =~ ${PMODULES_ROOT} ]]; then
|
|
std::info "\t${modulepath[i]}\n"
|
|
let n+=1
|
|
fi
|
|
done
|
|
if (( n == 0 )); then
|
|
std::info "\tnone\n"
|
|
fi
|
|
std::info "\n"
|
|
}
|
|
|
|
use () {
|
|
local arg=$1
|
|
|
|
if is_release "${arg}"; then
|
|
# argument is release
|
|
std::append_path UsedReleases "${arg}"
|
|
return
|
|
fi
|
|
if [[ "${arg}" =~ "flag=" ]]; then
|
|
# argument is flag
|
|
std::append_path UseFlags "${arg/flag=}"
|
|
return
|
|
fi
|
|
if [[ -z ${GroupDepths[${arg}]} ]] && [[ -d "${PMODULES_ROOT}/${arg}" ]]; then
|
|
scan_groups "${PMODULES_ROOT}"
|
|
fi
|
|
|
|
if [[ -n ${GroupDepths[${arg}]} ]] &&
|
|
(( ${GroupDepths[${arg}]} == 0 )); then
|
|
# argument is group in our root with depth 0
|
|
std::append_path UsedGroups "${arg}"
|
|
local dir="${PMODULES_ROOT}/${arg}/"
|
|
dir+="${PMODULES_MODULEFILES_DIR}"
|
|
${add2path_func} MODULEPATH "${dir}"
|
|
return
|
|
fi
|
|
if [[ -n ${GroupDepths[${arg}]} ]] &&
|
|
(( ${GroupDepths[${arg}]} > 0 )); then
|
|
# argument is a hierarchical group in our root
|
|
std::die 3 "%s %s: %s -- %s\n" \
|
|
"${CMD}" "${subcommand}" \
|
|
"illegal group" \
|
|
"${arg}"
|
|
return
|
|
fi
|
|
# arg must be a directory!
|
|
if [[ ! -d ${arg} ]]; then
|
|
std::die 3 "%s %s: %s -- %s\n" \
|
|
"${CMD}" "${subcommand}" \
|
|
"illegal argument" \
|
|
"${arg}"
|
|
return
|
|
fi
|
|
|
|
dir="$(cd "${arg}" && pwd)"
|
|
if [[ ${dir} =~ ^${PMODULES_ROOT} ]]; then
|
|
# argument is somehing in our root
|
|
std::die 3 "%s %s: %s -- %s\n" \
|
|
"${CMD}" "${subcommand}" \
|
|
"illegal argument" \
|
|
"${arg}"
|
|
return
|
|
fi
|
|
# argument is a modulepath
|
|
${add2path_func} MODULEPATH "$(cd "${arg}" && pwd)"
|
|
}
|
|
|
|
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'
|
|
;;
|
|
-- )
|
|
;;
|
|
* )
|
|
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...
|
|
#
|
|
Subcommands[unuse]='unuse'
|
|
Options[unuse]='-o H -l help'
|
|
Help[unuse]='
|
|
unuse directory|group|release...
|
|
Remove the given directory, group or release from the search
|
|
path.
|
|
'
|
|
|
|
subcommand_unuse() {
|
|
local -r subcommand='unuse'
|
|
unuse() {
|
|
local arg=$1
|
|
|
|
if is_release "${arg}"; then
|
|
# argument is release
|
|
std::remove_path UsedReleases "${arg}"
|
|
return
|
|
fi
|
|
if [[ "${arg}" =~ "flag=" ]]; then
|
|
# argument is flag
|
|
std::remove_path UseFlags "${arg/flag=}"
|
|
return
|
|
fi
|
|
if [[ -n ${GroupDepths[${arg}]} ]] &&
|
|
(( ${GroupDepths[${arg}]} == 0 )); then
|
|
# argument is group in our root with depth 0
|
|
local var="PMODULES_LOADED_${arg^^}"
|
|
if [[ -n "${!var}" ]]; then
|
|
std::die 3 "%s %s: %s -- %s\n" \
|
|
"${CMD}" "${subcommand}" \
|
|
"cannot remove group due to loaded modules" \
|
|
"${arg}"
|
|
fi
|
|
std::remove_path UsedGroups "${arg}"
|
|
local dir="${PMODULES_ROOT}/${arg}/"
|
|
dir+="${PMODULES_MODULEFILES_DIR}"
|
|
std::remove_path MODULEPATH "${dir}"
|
|
return
|
|
fi
|
|
if [[ -n ${GroupDepths[${arg}]} ]] &&
|
|
(( ${GroupDepths[${arg}]} > 0 )); then
|
|
# argument is a hierarchical group in our root
|
|
std::die 3 "%s %s: %s -- %s\n" \
|
|
"${CMD}" "${subcommand}" \
|
|
"illegal group" \
|
|
"${arg}"
|
|
return
|
|
fi
|
|
# arg must be a directory!
|
|
if [[ ! -d ${arg} ]]; then
|
|
std::die 3 "%s %s: %s -- %s\n" \
|
|
"${CMD}" "${subcommand}" \
|
|
"illegal argument" \
|
|
"${arg}"
|
|
return
|
|
fi
|
|
|
|
dir="$(cd "${arg}" && pwd)"
|
|
if [[ ${dir} =~ ^${PMODULES_ROOT} ]]; then
|
|
# argument is somehing in our root
|
|
std::die 3 "%s %s: %s -- %s\n" \
|
|
"${CMD}" "${subcommand}" \
|
|
"illegal argument" \
|
|
"${arg}"
|
|
return
|
|
fi
|
|
# argument is a modulepath
|
|
std::remove_path MODULEPATH "${dir}"
|
|
|
|
}
|
|
|
|
local -a args=()
|
|
while (( $# > 0)); do
|
|
case "$1" in
|
|
-H | --help )
|
|
print_help "${subcommand}"
|
|
;;
|
|
-- )
|
|
;;
|
|
* )
|
|
args+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
if (( ${#args[@]} == 0 )); then
|
|
std::die 3 "%s %s: %s\n" \
|
|
"${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 'update' "$@"
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# 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 'refresh' "$@"
|
|
}
|
|
|
|
reset_modulepath() {
|
|
MODULEPATH=''
|
|
local group
|
|
local root="${PMODULES_ROOT}"
|
|
for group in ${PMODULES_DEFAULT_GROUPS}; do
|
|
local dir="${root}/${group}/${PMODULES_MODULEFILES_DIR}"
|
|
[[ -d "${dir}" ]] && std::prepend_path MODULEPATH "${dir}"
|
|
done
|
|
}
|
|
|
|
reset_used_groups() {
|
|
UsedGroups=''
|
|
local group
|
|
for group in ${PMODULES_DEFAULT_GROUPS}; do
|
|
std::append_path UsedGroups "${group}"
|
|
done
|
|
g_env_must_be_saved='yes'
|
|
}
|
|
|
|
reset_used_releases() {
|
|
declare -g UsedReleases=''
|
|
for r in ${PMODULES_DEFAULT_RELEASES//:/ }; do
|
|
std::append_path UsedReleases "${r}"
|
|
done
|
|
g_env_must_be_saved='yes'
|
|
}
|
|
|
|
init_path() {
|
|
std::replace_path PATH "${PMODULES_HOME%/*}/.*"
|
|
std::prepend_path PATH "${PMODULES_HOME}/bin"
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
pmodules_init() {
|
|
declare -gx LOADEDMODULES=''
|
|
declare -gx _LMFILES_=''
|
|
declare -gx UsedGroups=''
|
|
declare -gx MODULEPATH=''
|
|
|
|
declare -Ag GroupDepths='()'
|
|
declare -g UseFlags=()
|
|
reset_modulepath
|
|
reset_used_groups
|
|
reset_used_releases
|
|
init_path
|
|
init_manpath
|
|
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() {
|
|
local -r subcommand='purge'
|
|
local -a args=()
|
|
while (( $# > 0)); do
|
|
case "$1" in
|
|
-H | --help )
|
|
print_help "${subcommand}"
|
|
;;
|
|
-- )
|
|
;;
|
|
* )
|
|
args+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
if (( ${#args[@]} > 0 )); then
|
|
std::die 3 "%s %s: %s\n" \
|
|
"${CMD}" "${subcommand}" \
|
|
"no arguments allowd"
|
|
fi
|
|
"${modulecmd}" "${Shell}" "${subcommand}"
|
|
reset_modulepath
|
|
reset_used_groups
|
|
export_env MODULEPATH
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# 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() {
|
|
local -r subcommand='list'
|
|
local opts=()
|
|
local args=()
|
|
while (( $# > 0 )); do
|
|
case $1 in
|
|
-H | --help )
|
|
print_help "${subcommand}"
|
|
;;
|
|
-h | --human )
|
|
opts+=( '-h' )
|
|
;;
|
|
-l | --long )
|
|
opts+=( '-l' )
|
|
;;
|
|
-t | --terse )
|
|
opts+=( '-t' )
|
|
;;
|
|
-- )
|
|
;;
|
|
* )
|
|
args+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
if (( ${#args[@]} > 0 )); then
|
|
std::die 3 "%s %s: %s\n" \
|
|
"${CMD}" "${subcommand}" \
|
|
"no arguments allowd"
|
|
fi
|
|
"${modulecmd}" "${Shell}" list "${opts[@]}"
|
|
}
|
|
|
|
|
|
##############################################################################
|
|
#
|
|
# 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 -r subcommand='clear'
|
|
local -a args=()
|
|
while (( $# > 0 )); do
|
|
case $1 in
|
|
-H | --help )
|
|
print_help "${subcommand}"
|
|
;;
|
|
-- )
|
|
:
|
|
;;
|
|
* )
|
|
args+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
if (( ${#args[@]} > 0 )); then
|
|
std::die 3 "%s %s: %s\n" \
|
|
"${CMD}" "${subcommand}" \
|
|
"no arguments allowed"
|
|
fi
|
|
pmodules_init
|
|
export_env LOADEDMODULES MODULEPATH _LMFILES_
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# search [switches] [STRING...]
|
|
#
|
|
Subcommands[search]='search'
|
|
Options[search]='-o aH -l help -l no-header -l print-modulefiles '
|
|
Options[search]+='-l release: -l with: -l all-releases -l src: -l print-csv'
|
|
Help[search]='
|
|
USAGE:
|
|
module search [switches] string...
|
|
Search installed modules. If an argument is given, search
|
|
for modules whose name match the argument.
|
|
|
|
SWITCHES:
|
|
--no-header
|
|
Suppress output of a header.
|
|
|
|
--release=RELEASE
|
|
Search for modules within this release. You can specify this
|
|
switch multiple times. Without this switch, the used releases
|
|
will be searched.
|
|
|
|
-a|--all-releases
|
|
Search within all releases.
|
|
|
|
--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.
|
|
'
|
|
|
|
subcommand_search() {
|
|
local -r subcommand='search'
|
|
local modules=()
|
|
local with_modules='//'
|
|
local src_prefix=''
|
|
local opt_print_header='yes'
|
|
local opt_print_modulefiles='no'
|
|
local opt_print_csv='no'
|
|
local opt_use_releases=':'
|
|
local -r fmt="%-20s %-10s %-12s %-s\n"
|
|
|
|
# no args
|
|
print_header() {
|
|
printf '\n' 1>&1
|
|
printf "${fmt}" "Module" "Release" "Group" "Requires" 1>&2
|
|
printf -- '-%.0s' {1..60} 1>&2
|
|
printf '\n' 1>&2
|
|
}
|
|
|
|
#.....................................................................
|
|
#
|
|
# output result of search
|
|
# Args:
|
|
# $1: tmp file
|
|
#
|
|
# variables used from enclosing function:
|
|
# opt_print_header
|
|
# opt_print_modulefiles
|
|
# with_modules
|
|
#
|
|
print_result() {
|
|
local -r tmpfile=$1
|
|
[[ "${opt_print_header}" == "yes" ]] && print_header
|
|
if [[ "${opt_print_modulefiles}" == "yes" ]]; then
|
|
while read -a line; do
|
|
# group first
|
|
local out="${line[2]}/"
|
|
# add directory of modulefiles
|
|
out+="${PMODULES_MODULEFILES_DIR}/"
|
|
for d in "${line[@]:3}"; do
|
|
out+="$d/"
|
|
done
|
|
out+="${line[0]}"
|
|
std::info "${out}\n"
|
|
done < <("${sort}" -k 1,1 -k 4,4 -k 5,5 "${tmpfile}" | \
|
|
awk "${with_modules}")
|
|
elif [[ "${opt_print_csv}" == "yes" ]]; then
|
|
while read -a toks; do
|
|
:
|
|
done < <("${sort}" -k 1,1 -k 4,4 -k 5,5 "${tmpfile}" | \
|
|
awk "${with_modules}")
|
|
else
|
|
"${sort}" -k 1,1 -k 4,4 -k 5,5 "${tmpfile}" | \
|
|
awk "${with_modules}" 1>&2
|
|
fi
|
|
}
|
|
#.....................................................................
|
|
#
|
|
# search modules
|
|
# Args:
|
|
# $1: module name pattern
|
|
#
|
|
# Variables used from enclosing function
|
|
# :FIXME:
|
|
#
|
|
search () {
|
|
local -r module=$1
|
|
# write results to a temporary file for later processing
|
|
local -r tmpfile=$( "${mktemp}" /tmp/Pmodules.XXXXXX ) \
|
|
|| std::die 1 "Oops: unable to create tmp file!\n"
|
|
local group
|
|
# loop over all groups
|
|
for group in "${!GroupDepths[@]}"; do
|
|
# loop over all directories which can be added to
|
|
# MODULEPATH inside current group
|
|
local depth=${GroupDepths[${group}]}
|
|
local mpaths=( $(find \
|
|
"${src_prefix}/${group}/modulefiles" \
|
|
-type d \
|
|
-mindepth ${depth} -maxdepth ${depth} \
|
|
2>/dev/null))
|
|
local mpath
|
|
for mpath in "${mpaths[@]}"; do
|
|
# get dependencies encoded in directory name
|
|
local p="${mpath/${src_prefix}}"
|
|
p=( ${p//\// } )
|
|
local deps=()
|
|
local -i i
|
|
for ((i=2; i < ${#p[@]}; i+=2)); do
|
|
deps+=( ${p[i]}/${p[i+1]} )
|
|
done
|
|
local requires=${deps[@]}
|
|
|
|
# get and print all available modules in $mpath
|
|
# with respect to the requested releases
|
|
# tmpfile: module/version release group group-
|
|
# dependencies...
|
|
local mods=( $( get_available_modules \
|
|
"${mpath}" \
|
|
"${module}" \
|
|
"${opt_use_releases}" ) )
|
|
[[ ${#mods[@]} == 0 ]] && continue
|
|
for (( i=0; i<${#mods[@]}; i+=2 )); do
|
|
printf "${fmt}" ${mods[i]} "${mods[i+1]}" \
|
|
${group} "${requires}" >> "${tmpfile}"
|
|
done
|
|
done
|
|
done
|
|
print_result "${tmpfile}"
|
|
rm -f "${tmpfile}"
|
|
}
|
|
|
|
while (( $# > 0 )); do
|
|
case $1 in
|
|
-H | --help )
|
|
print_help "${subcommand}"
|
|
;;
|
|
--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 | --release=* )
|
|
if [[ "$1" == "--release" ]]; then
|
|
local arg=$2
|
|
shift
|
|
else
|
|
local arg=${1/--release=}
|
|
fi
|
|
is_release "${arg}" || \
|
|
std::die 1 "%s %s: %s -- %s\n" \
|
|
"${CMD}" 'search' \
|
|
"illegal release name" \
|
|
"${arg}"
|
|
opt_use_releases+="${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\n" \
|
|
"${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 )
|
|
opt_use_releases="${PMODULES_DEFINED_RELEASES}"
|
|
;;
|
|
--src )
|
|
src_prefix=$2
|
|
pmodules::check_directories "${src_prefix}"
|
|
shift
|
|
;;
|
|
-- )
|
|
;;
|
|
* )
|
|
modules+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
if [[ -z "${src_prefix}" ]]; then
|
|
src_prefix="${PMODULES_ROOT}"
|
|
fi
|
|
|
|
if [[ "${opt_use_releases}" == ":" ]]; then
|
|
opt_use_releases=":${UsedReleases}:"
|
|
fi
|
|
|
|
if [[ ${#modules[@]} == 0 ]]; then
|
|
modules+=( '' )
|
|
fi
|
|
|
|
if (( ${#GroupDepths[@]} == 0 )) || \
|
|
[[ ${src_prefix} != ${PMODULES_ROOT} ]]; then
|
|
scan_groups "${src_prefix}"
|
|
fi
|
|
|
|
local module
|
|
for module in "${modules[@]}"; do
|
|
search "${module}"
|
|
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
|
|
|
|
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 -r subcommand='help'
|
|
local -a args=()
|
|
while (( $# > 0 )); do
|
|
case $1 in
|
|
-[hH] | --help )
|
|
print_help "${subcommand}"
|
|
;;
|
|
-V | --version )
|
|
print_help 'version'
|
|
;;
|
|
-- )
|
|
:
|
|
;;
|
|
* )
|
|
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 UsedReleases)
|
|
"${modulecmd}" "${Shell}" "${subcommand}" "${arg}"
|
|
fi
|
|
done
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# whatis
|
|
#
|
|
Subcommands[whatis]='whatis'
|
|
Options[whatis]='-o H -l help'
|
|
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() {
|
|
if (( $# == 0 )); then
|
|
subcommand_generic0 'whatis'
|
|
else
|
|
subcommand_generic1plus 'whatis' "$@"
|
|
fi
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# apropos
|
|
#
|
|
Subcommands[apropos]='apropos'
|
|
Subcommands[keyword]='apropos'
|
|
Options[apropos]='-o H -l help'
|
|
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() {
|
|
subcommand_generic1 'apropos' "$@"
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# 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 'initadd' "$@"
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# 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 'initprepend' "$@"
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# 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 'initrm' "$@"
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# 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() {
|
|
subcommand='initswitch'
|
|
local args=()
|
|
while (( $# > 0 )); do
|
|
case $1 in
|
|
-h | --help )
|
|
print_help "${subcommand}"
|
|
;;
|
|
-- )
|
|
:
|
|
;;
|
|
* )
|
|
args+=( "$1" )
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
if (( ${#args[@]} != 2 )); then
|
|
std::die 3 "%s %s: %s\n" \
|
|
"${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 'initlist' "$@"
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# 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 'initclear' "$@"
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# main
|
|
#
|
|
case "$1" in
|
|
sh | bash | zsh )
|
|
declare Shell="sh"
|
|
;;
|
|
csh | tcsh )
|
|
declare Shell='csh'
|
|
;;
|
|
* )
|
|
std::die 1 "${CMD}: unsupported shell -- $1\n"
|
|
;;
|
|
esac
|
|
shift
|
|
|
|
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.\n"
|
|
fi
|
|
|
|
if [[ -z "${Subcommands[${subcommand}]}" ]]; then
|
|
std::die 1 "${CMD}: unknown sub-command -- ${subcommand}\n"
|
|
fi
|
|
|
|
if [[ -n ${PMODULES_ENV} ]]; then
|
|
eval "$("${base64}" -d <<< "${PMODULES_ENV}" 2>/dev/null)"
|
|
else
|
|
pmodules_init
|
|
fi
|
|
|
|
if (( ${#GroupDepths[@]} == 0 )); then
|
|
scan_groups "${PMODULES_ROOT}"
|
|
fi
|
|
|
|
declare options
|
|
options=$( "${getopt}" ${Options[${subcommand}]} -- -- "${opts[@]}" "$@" ) \
|
|
|| print_help "${subcommand}"
|
|
eval set -- ${options}
|
|
subcommand_${Subcommands[$subcommand]} "$@"
|
|
|
|
# Local Variables:
|
|
# mode: sh
|
|
# sh-basic-offset: 8
|
|
# tab-width: 8
|
|
# End:
|