Files
Pmodules/Pmodules/modulecmd.bash.in
T
2020-10-27 09:30:00 +01:00

2361 lines
67 KiB
Bash

#!@PMODULES_HOME@/sbin/bash --noprofile
#
#set -o nounset
# we have to unset CDPATH, otherwise 'cd' prints the directoy!
unset CDPATH
# used in some output messages 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 )
vars+=( PMODULES_OVERLAYS )
vars+=( Overlays )
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 module outside ${Overlays[@]} is always 'stable'
# - the release of a module inside ${Overlays[@]} without a
# coresponding release file is always 'unstable'
#
# Args:
# $1 upvar for returned release
# $2 absolute modulefile name
# $3 colon seperated list of accepted releases
#
get_release() {
local "$1"
local -r modulefile=$2
local -r releases=$3
local overlay
# is modulefile in an used overlay?
for overlay in "${!Overlays[@]}" 'ZZZZZZZ'; do
[[ ${modulefile} =~ ^${overlay}/ ]] && break
done
if [[ "${overlay}" == 'ZZZZZZZ' ]]; then
# this modulefile is *not* in an used overlay
# => it is stable be default
std::upvar $1 'stable'
return 0
fi
#
# In an overlay the name of the module-file is something like
# dir/modulefiles/name/version
# the corresponding release file is
# dir/modulefiles/name/.release-version
#
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
[[ :${releases}: =~ ${release} ]]
}
is_release() {
[[ ${PMODULES_DEFINED_RELEASES} =~ :$1: ]]
}
get_overlay_of_group () {
local "$1"
local -r group="$2"
local overlay
for overlay in "${!Overlays[@]}"; do
if [[ -d "${overlay}/${group}/${PMODULES_MODULEFILES_DIR}" ]]; then
std::upvar $1 "${overlay}"
return 0
fi
done
return 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 overlay=''
get_overlay_of_group overlay "${group}" || return 1
local moduledir="${overlay}/${group}/${PMODULES_MODULEFILES_DIR}"
[[ -d "${moduledir}" ]] || return 1
compute_group_depth "${moduledir}"
}
#
# Check whether a given path is in an used overlay.
# If yes, return 0 and the overlay with upvar of first argument
# otherwise return 1
#
# $1 upvar to return overlay
# $2 upvar to return group
# $3 path to check
#
find_overlay () {
local "$1"
local "$2"
local -r path=$3
local overlay
for overlay in "${!Overlays[@]}"; do
if [[ ${path}/ =~ ^${overlay}/ ]]; then
std::upvar $1 "${overlay}"
local group="${path#${overlay}/}"
group=${group%%/*}
std::upvar $2 "${group}"
return 0
fi
done
return 1
}
module_is_loaded() {
[[ :${LOADEDMODULES}: =~ :$1: ]]
}
#
# 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'
#
# 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
[[ -z ${line} ]] && continue
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=()
local opts=()
local overlay
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
local saved_IFS="${IFS}";
IFS=':'
local -a modulepath=(${MODULEPATH})
IFS=${saved_IFS}
local m=''
for m in "${args[@]}"; do
if [[ "$m" =~ ":" ]]; then
# $m is an extendet module name
# the format is one of
# - group:name
# - group:name:release
# - release:name
# - release:group:name
# - name:release
#
# :FIXME: move to a function
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=""
modulepath=()
for overlay in "${!Overlays[@]}"; do
MODULEPATH+=":${overlay}/${group}/"
MODULEPATH+="${PMODULES_MODULEFILES_DIR}"
modulepath+=( ${MODULEPATH} )
done
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 current_modulefile=''
local release=''
if ! find_module current_modulefile release "${MODULEPATH}" "${m}"; then
if [[ -z ${current_modulefile} ]]; then
std::info "%s %s: module does not exist -- %s\n" \
"${CMD}" 'load' "${m}"
std::die 3 ""
else
std::info "%s %s: module unavailable -- %s\n" \
"${CMD}" 'load' "${m}"
[[ ${verbosity_lvl} == 'verbose' ]] && output_load_hints
std::die 3 ""
fi
fi
if [[ ":${LOADEDMODULES}:" =~ ":${m}:" ]]; then
# already loaded
continue
fi
local prefix=$("${modulecmd}" 'bash' 'show' "${current_modulefile}" 2>&1 | \
awk '/_PREFIX |_HOME / {print $3; exit}')
if [[ -n ${prefix} ]]; then
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 base 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
}
#
# Find all modules in a given modulepath matching a specific string.
# The search can be restricted to certain releases.
#
# $1 modulepath (colon separated)
# $2 string (module name or part of, might be empty)
# $3 releases, defaults to used releases
#
# return list like
# modulename1 release1 modulename2 release2 ...
#
get_available_modules() {
local saved_IFS=${IFS};
IFS=':'
local -a dirs=($1)
IFS=${saved_IFS}
local -r module="$2"
local -r releases="${3:-${UsedReleases}}"
local -a mods=()
local release
local -A dict
for dir in "${dirs[@]}"; do
test -d "${dir}" || continue
{
cd "${dir}"
local dirs=$(echo *)
# if no modules are installed in ${dir}, '*' expands to
# the empty string! Using '*' in the find command below
# would cause problems with some non-GNU find
# implementations.
if [[ -n ${dirs} ]]; then
while read mod; do
get_release release "${dir}/${mod}" "${releases}" || \
continue
if [[ -z ${dict[${mod}]} ]]; then
mods+=( "${mod}" ${release} )
dict[${mod}]=1
fi
done < <(find ${dirs} \( -type f -o -type l \) -not -name ".*" \
-ipath "${module}*")
fi
}
done
echo "${mods[@]}"
}
#
# find module(file) to load. Input arguments are
# $1 upvar: return module file
# $2 upvar: return module release
# $3 a modulepath (usually MODULEPATH)
# $4 module to load
#
# The module name can be
# name
# name/version
# name/version_flag
#
# Return
# return code 0 and modulefile in first argument
# return code 1 and modulefile in first argument
# if module
# return code 2
# if no modulefile could be found
#
find_module() {
local saved_IFS=${IFS};
IFS=':'
local -a dirs=($3)
IFS=${saved_IFS}
local -r module="$4"
for dir in "${dirs[@]}"; do
test -d "${dir}" || continue
local -i col=$((${#dir} + 2 ))
local -a modules
local -a releases
if [[ ${module} == */* ]]; then
# a version number has been specified. But we still might
# have the same module/version with different use flags.
# Releases we ignore in this case.
modules=$(find "${dir}" -type f -not -name ".*" \
-ipath "${dir}/${module}*" \
| cut -b${col}-)
for mod in "${modules[@]}"; do
#
# loop over all used flags. If a module with
# a used flag is available load this module.
for flag in "${UseFlags[@]/#/_}" ""; do
if [[ ${mod} == ${module}${flag} ]]; then
std::upvar $1 "${dir}/${mod}"
get_release release \
"${dir}/${mod}" \
"${UsedReleases}"
std::upvar $2 "${release}"
return 0
fi
done
done
else
# no version has been specified. This makes it more
# difficult. We have to load the newest version taking
# the used releases and flags into account.
(( col += ${#module} + 1 ))
modules=( $(find "${dir}" -type f -not -name ".*" \
-ipath "${dir}/${module}/*" \
| cut -b${col}- \
| sort -rV ) )
modules=( "${modules[@]/#/${module}/}" )
releases=":${UsedReleases}:"
for mod in "${modules[@]}"; do
#
# loop over all used flags. If a module with
# a used flag is available load this module.
local release=''
for flag in "${UseFlags[@]/#/_}" ""; do
if [[ ${mod} == ${module}/*${flag} ]]; then
std::upvar $1 "${dir}/${mod}"
get_release release \
"${dir}/${mod}" \
"${UsedReleases}"
std::upvar $2 "${release}"
[[ :${release}: =~ :${UsedReleases}: ]] && \
return 0
fi
done
done
fi
done
return 1
}
##############################################################################
#
# 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=$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"
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 "$1"
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 "$1"
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 -a modulepath
local saved_IFS=${IFS}
IFS=':'
modulepath=(${MODULEPATH})
IFS=${saved_IFS}
local overlay=''
local group=''
local -A modulepath_of_group
local -a groups=()
for dir in "${modulepath[@]}"; do
group='other'
find_overlay overlay group "${dir}"
if [[ ${modulepath_of_group[${group}]} ]]; then
modulepath_of_group[${group}]+=:${dir}
else
modulepath_of_group[${group}]=${dir}
groups+=( "${group}" )
fi
done
local string
for string in "${pattern[@]}"; do
for group in "${groups[@]}"; do
mods=( $( get_available_modules \
"${modulepath_of_group[${group}]}" \
"${string}" \
"${opt_use_releases}" ) )
[[ ${#mods[@]} == 0 ]] && continue
${output_function} "${group}"
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 overlays and compute group depth's
#
# Args:
# $1: array of overlays
#
scan_groups () {
local -r overlays=( "$@" )
local overlay
for overlay in "${overlays[@]}"; do
local moduledir
for moduledir in ${overlay}/*/${PMODULES_MODULEFILES_DIR}; do
compute_group_depth "${moduledir}"
done
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
local overlay
std::info "\nUsed overlays:\n"
for overlay in "${!Overlays[@]}"; do
std::info "\t${overlay}\n"
done
std::info "\nAdditonal directories in MODULEPATH:\n"
let n=0
local group
for (( i=0; i<${#modulepath[@]}; i++)); do
if ! find_overlay overlay group "${modulepath[i]}"; 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 () {
use_overlay() {
local overlay="$1"
[[ -d "${overlay}" ]] || \
std::die 3 "%s %s: %s -- %s\n" \
"${CMD}" "${subcommand}" \
"is not an overlay directory" \
"${overlay}"
if [[ ! ${Overlays[${overlay}]} ]]; then
Overlays[${overlay}]=1
PMODULES_OVERLAYS+=:${overlay}
export_env PMODULES_OVERLAYS
g_env_must_be_saved='yes'
scan_groups "${!Overlays[@]}"
local group
for group in ${UsedGroups//:/ }; do
local dir="${overlay}/"
dir+="${group}/${PMODULES_MODULEFILES_DIR}"
if [[ -d "${dir}" ]]; then
std::prepend_path MODULEPATH "${dir}"
fi
done
else
std::info "%s %s: %s -- %s\n" \
"${CMD}" "${subcommand}" \
"overlay already in use" \
"${overlay}"
fi
}
use_group() {
std::append_path UsedGroups "$1"
local overlay group
for overlay in "${!Overlays[@]}"; do
for group in ${UsedGroups//:/ }; do
local dir="${overlay}/"
dir+="${group}/${PMODULES_MODULEFILES_DIR}"
if [[ -d "${dir}" ]]; then
std::prepend_path MODULEPATH "${dir}"
fi
done
done
}
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
UseFlags["${arg/flag=}"]=1
return
fi
if [[ "${arg}" =~ "overlay=" ]]; then
local overlay="${arg/overlay=}"
use_overlay "${overlay}"
return
fi
# check whether the user wants to add an unused group in
# an used overlay.
if [[ -z ${GroupDepths[${arg}]} ]]; then
# this scan is required if a new group has been
# create inside an used overlay
scan_groups "${overlays}"
fi
if [[ -n ${GroupDepths[${arg}]} ]] &&
(( ${GroupDepths[${arg}]} == 0 )); then
# argument is group with depth 0
use_group "${arg}"
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
local dir="$(cd "${arg}" && pwd)"
local overlay
for overlay in "${!Overlays[@]}"; do
if [[ ${dir}/ =~ ^${overlay}/ ]]; then
# dir is in one of our used overlays
std::die 3 "%s %s: %s -- %s\n" \
"${CMD}" "${subcommand}" \
"illegal argument" \
"${arg}"
return
fi
done
# argument is a modulepath
${add2path_func} MODULEPATH "${dir}"
}
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'
local saved_IFS=${IFS};
IFS=':'
local -a modulepath=(${MODULEPATH})
IFS=${saved_IFS}
unuse() {
unuse_overlay() {
[[ -d "${overlay}" ]] || \
std::die 3 "%s %s: %s -- %s\n" \
"${CMD}" "${subcommand}" \
"is not an overlay directory" \
"${overlay}"
[[ "${overlay}" == "${PMODULES_ROOT}" ]] && \
std::die 3 "%s %s: %s -- %s\n" \
"${CMD}" "${subcommand}" \
"cannot remove root overlay" \
"${overlay}"
if [[ ${Overlays[${overlay}]} ]]; then
[[ "${_LMFILES_}" =~ "${overlay}" ]] && \
std::die 3 "%s %s: %s -- %s\n" \
"${CMD}" "${subcommand}" \
"cannot remove overlay" \
"${overlay}"
unset "Overlays[${overlay}]"
std::remove_path PMODULES_OVERLAYS "${overlay}"
g_env_must_be_saved='yes'
export_env PMODULES_OVERLAYS
local dir
for dir in "${modulepath[@]}"; do
if [[ "${dir}" =~ "${overlay}" ]]; then
std::remove_path MODULEPATH "${dir}"
fi
done
fi
}
unuse_group() {
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 overlay
for overlay in "${!Overlays[@]}"; do
local dir="${overlay}/${arg}/${PMODULES_MODULEFILES_DIR}"
std::remove_path MODULEPATH "${dir}"
done
}
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
unset UseFlags["${arg/flag=}"]
return
fi
if [[ ${arg} =~ ^overlay= ]]; then
local overlay="${arg/overlay=}"
unuse_overlay "${overlay}"
return
fi
if [[ -n ${GroupDepths[${arg}]} ]] &&
(( ${GroupDepths[${arg}]} == 0 )); then
# argument is group in our root with depth 0
unuse_group "${arg}"
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
# user wants to append a directory to MODULEPATH
if [[ ! -d ${arg} ]]; then
std::die 3 "%s %s: %s -- %s\n" \
"${CMD}" "${subcommand}" \
"illegal argument" \
"${arg}"
return
fi
local dir="$(cd "${arg}" && pwd)"
local overlay
for overlay in "${!Overlays[@]}"; do
if [[ ${dir}/ =~ ^${overlay}/ ]]; then
# dir is in one of our used overlays
std::die 3 "%s %s: %s -- %s\n" \
"${CMD}" "${subcommand}" \
"illegal argument" \
"${arg}"
return
fi
done
# 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 overlay
for overlay in "${!Overlays[@]}"; do
for group in ${PMODULES_DEFAULT_GROUPS}; do
local dir="${overlay}/${group}/${PMODULES_MODULEFILES_DIR}"
[[ -d "${dir}" ]] && std::prepend_path MODULEPATH "${dir}"
done
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 -Ag Overlays=([${PMODULES_ROOT}]="1")
declare -gx PMODULES_OVERLAYS="${PMODULES_ROOT}"
declare -g UsedGroups=''
declare -gx MODULEPATH=''
declare -Ag GroupDepths='()'
declare -Ag UseFlags=()
reset_modulepath
reset_used_groups
reset_used_releases
init_path
init_manpath
export_env \
PMODULES_OVERLAYS \
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}"
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 '
Options[search]+='-l verbose'
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.
--verbose
vebose output
'
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_print_verbose='no'
local opt_use_releases=':'
#.....................................................................
#
# 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=''
# no args
print_header_default() {
std::info '\n'
std::info "${fmt}" "Module" "Release" "Group" "Requires"
std::info '-%.0s' {1..60}
std::info '\n'
}
print_line_default() {
std::info "${fmt}" "$1" "$2" "$3" "${@:5}"
}
print_default() {
fmt="%-20s %-10s %-12s %-s\n"
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_verbose() {
std::info '\n'
std::info "${fmt}" "Module" "Release" "Group" "Overlay" "Requires"
std::info '-%.0s' {1..79}
std::info '\n'
}
print_line_verbose() {
std::info "${fmt}" "$@"
}
print_verbose() {
fmt="%-20s %-10s %-12s %-20s %-s\n"
func_print_header='print_header_verbose'
func_print_line='print_line_verbose'
}
print_header_none() {
:
}
print_line_modulefile() {
local line=( "$@" )
# 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"
}
# print full modulefile names only
print_modulefiles() {
fmt=''
func_print_header='print_header_none'
func_print_line='print_header_none'
}
print_line_csv() {
:
}
print_csv() {
fmt=''
func_print_header='print_header_none'
func_print_line='print_line_csv'
}
local -r tmpfile=$1
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
${func_print_header}
while read -a toks; do
${func_print_line} "${toks[@]}"
done < <("${sort}" -k 1,1 -k 4,4 -k 5,5 "${tmpfile}" | \
awk "${with_modules}")
}
#.....................................................................
#
# 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
local overlay
local unused
for mpath in "${mpaths[@]}"; do
# get dependencies encoded in directory name
find_overlay overlay unused "${mpath}"
local p="${mpath/${overlay}}"
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
echo ${mods[i]} ${mods[i+1]} \
${group} ${overlay} \
${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
;;
-v | --verbose )
opt_print_verbose='yes'
;;
-- )
;;
* )
modules+=( "$1" )
;;
esac
shift
done
if [[ -z "${src_prefix}" ]]; then
src_prefix=( "${!Overlays[@]}" )
fi
if [[ "${opt_use_releases}" == ":" ]]; then
opt_use_releases=":${UsedReleases}:"
fi
if [[ ${#modules[@]} == 0 ]]; then
modules+=( '' )
fi
# :FIXME: do we need this?
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 "${!Overlays[@]}"
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: