diff --git a/Pmodules/modulecmd.bash.in.overlays b/Pmodules/modulecmd.bash.in.overlays new file mode 100644 index 0000000..e2c0132 --- /dev/null +++ b/Pmodules/modulecmd.bash.in.overlays @@ -0,0 +1,2087 @@ +#!@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" + +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 + +# required for pre 0.99.3 modulefiles +declare -rx PSI_LIBMODULES="${TCLLIBPATH}/libmodules.tcl" + +declare verbosity_lvl=${PMODULES_VERBOSITY:-'verbose'} + +shopt -s nullglob + +declare -A GroupDepths='()' +declare current_modulefile='' +declare g_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() { + local -r shell="$1" + shift + case "${shell}" in + 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_env() { + local -r shell="$1" + shift + local s='' + local tmp + while (( $# > 0 )); do + tmp="$( typeset -p $1 2> /dev/null)" + [[ -n "${tmp}" ]] && s+="${tmp};" + shift + done + declare -g PMODULES_ENV=$( "${base64}" --wrap=0 <<< "$s" ) + export_env ${shell} PMODULES_ENV +} + +trap 'save_env ${g_shell} GroupDepths UsedReleases UseFlags UsedGroups PMODULES_DEFAULT_GROUPS PMODULES_DEFINED_RELEASES PMODULES_DEFAULT_RELEASES' 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 + + # is modulefile in a used overlay? + for root in "${!Overlays[@]}" 'ZZZZZZZ'; do + [[ ${modulefile} =~ ${root} ]] && break + done + if [[ "${root}" == 'ZZZZZZZ' ]]; then + std::upvar $1 'stable' + return 0 + fi + + # we are inside the used overlays + 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} ]] +} + +: ${PMODULES_DEFINED_RELEASES:=':unstable:stable:deprecated:'} + +is_release() { + [[ ${PMODULES_DEFINED_RELEASES} =~ :$1: ]] +} + +is_used_release() { + [[ ":${UsedReleases}:" =~ :$1: ]] +} + +get_overlay_of_group () { + local "$1" + local -r group="$2" + for overlay in "${!Overlays[@]}"; do + if [[ -d "${overlay}/${group}/${PMODULES_MODULEFILES_DIR}" ]]; then + std::upvar $1 "${overlay}" + return 0 + fi + done + return 1 +} + +is_group () { + local -r group="$1" + # arg isn't emtpy and group already in cache + [[ -n ${group} ]] && [[ -n ${GroupDepths[${group}]} ]] && return 0 + + # not yet cached or not a group + local overlay='' + get_overlay_of_group overlay "${group}" || return 1 + get_group_depths "${overlay}" "${group}" +} + +# +# Check whether a given path is in an 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 + 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 +} + +is_used_group() { + [[ :${UsedGroups}: =~ :$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: no arguments allowed\n" \ + "${CMD}" "${subcommand}" + fi + "${modulecmd}" "${g_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: missing argument\n" \ + "${CMD}" "${subcommand}" + elif (( ${#args[@]} > 1 )); then + std::die 3 "%s %s: only one argument allowed\n" \ + "${CMD}" "${subcommand}" + fi + "${modulecmd}" "${g_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: missing argument\n" \ + "${CMD}" "${subcommand}" + fi + "${modulecmd}" "${g_shell}" "${subcommand}" "${args[@]}" +} + +############################################################################## +# +# load [-fsvw] +# +# $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 + # + # Notes: + # The variable 'release' in function 'subcommand_load()' will be set. + # + 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}" "${shell}" 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 + 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 + } + + load_dependencies() { + local -r fname="$1" + while read dep; do + [[ -z ${dep} ]] && continue + [[ ${dep:0:1} == \# ]] && continue + module_is_loaded "${dep}" && continue + local output=$( subcommand_load --internal "${dep}") + echo ${output} + eval ${output} + done < "${fname}" + } + + local args=() + opts=() + local shell="${g_shell}" + 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' + ;; + -i | --internal ) + shell='bash' + ;; + -- ) + ;; + * ) + args+=( $1 ) + ;; + esac + shift + done + if (( ${#args[@]} == 0 )); then + std::die 2 "${CMD} load: No module specified\n" + fi + for m in "${args[@]}"; do + if [[ "$m" =~ ":" ]]; then + + # $m is an extendet module + # the format is one of + # - group:name + # - group:name:release + # - release:name + # - release:group:name + # - 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: illegal group name -- %s\n" \ + "${CMD}" 'load' "${group}" + local -i depth=${GroupDepths[${group}]} + (( depth != 0 )) && \ + std::die 3 "%s %s: illegal group name -- %s\n" \ + "${CMD}" 'load' "${group}" + MODULEPATH="" + modulepath=() + for root in "${!Overlays[@]}"; do + MODULEPATH+="${root}/${group}/${PMODULES_MODULEFILES_DIR}:" + modulepath+=( ${MODULEPATH} ) + done + fi + if [[ -n ${release} ]]; then + is_release "${release}" || \ + std::die 3 "%s %s: illegal release name -- %s\n" \ + "${CMD}" 'load' "${release}" + std::append_path UsedReleases "${release}" + fi + fi + local found='' + for flag in "${UseFlags[@]/#/_}" ""; do + 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 [[ ":${LOADEDMODULES}:" =~ ":${m}:" ]]; then + std::die 3 "%s %s: module already loaded -- %s\n" \ + "${CMD}" 'load' "${m}" + fi + for root in "${!Overlays[@]}"; do + if [[ ${current_modulefile} =~ ${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 "$_" + break + fi + done + local tmpfile=$( "${mktemp}" /tmp/Pmodules.XXXXXX ) \ + || std::die 1 "Oops: unable to create tmp file!\n" + local output=$("${modulecmd}" "${shell}" ${opts} load "${current_modulefile}" 2> "${tmpfile}") + echo "${output}" + eval "${output}" + + # 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}" 'load' "${error_txt}" "${m}" + elif [[ -n ${error} ]]; then + echo "${error}" 1>&2 + fi + if [[ ${verbosity_lvl} != silent ]] && [[ ${release} != stable ]]; then + std::info "%s %s: a %s module has been loaded -- %s\n" \ + "${CMD}" 'load' ${release} "${m}" + fi + done + # fix LOADEDMODULES + LOADEDMODULES="${_LMFILES_}" + local dir + while read dir; do + [[ "${dir: -1}" == "/" ]] || dir+="/" + LOADEDMODULES="${LOADEDMODULES//${dir}}" + done <<< "${MODULEPATH//:/$'\n'}" + export_env "${g_shell}" 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: missing argument\n" \ + "${CMD}" 'unload' + fi + + local arg + for arg in "${args[@]}"; do + local output=$("${modulecmd}" "${g_shell}" 'unload' "${arg}") + echo "${output}" + eval "${output}" + done +} + +############################################################################## +# +# swap [] +# +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: missing argument\n" \ + "${CMD}" 'swap' + elif (( ${#args[@]} > 2 )); then + std::die 3 "%s %s: too many arguments\n" \ + "${CMD}" 'swap' + 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 +# +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: missing argument\n" \ + "${CMD}" "${subcommand}" + fi + + local arg + for arg in "${args[@]}"; do + "${modulecmd}" "${g_shell}" "${subcommand}" "${arg}" + done +} + +# +# Get all available modules in given modulepath. Whereby modulepath is +# a colon separated list. +# 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}" + 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 * \( -type f -o -type l \) -not -name ".*" -ipath "${module}*") + } + done + echo "${mods[@]}" +} + +############################################################################## +# +# avail [-hlt] [...] +# +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 [[ "${opt_all_groups}" = 'yes' ]]; then + rescan_groups "${!Overlays[@]}" + fi + 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 depths or passed group +# Note: cwd must be Pmodules root directory +# $1: group +# +get_group_depth () { + local -r group="$1" + local -r dir="${group}/${PMODULES_MODULEFILES_DIR}" + test -d "${dir}" || return 1 + local tmp=$(find "${dir}" -depth -type f -o -type l 2>/dev/null| head -1) + local -a tmp2=( ${tmp//\// } ) + local depth=${#tmp2[@]} + (( depth-=4 )) + # if a group doesn't contain a module, the above computed depth is < 0. + # We set it to 0 (even this might be wrong). + # :FIXME: better solution? + (( depth < 0 )) && (( depth = 0 )) + GroupDepths[$group]=${depth} +} + +# +# Compute depth for all known groups +# $1: root of modulefile hierarchy +get_group_depths () { + local -r roots=( "$@" ) + local root + for root in "${roots[@]}"; do + { + cd "${root}" + local group + # for some unknown reason [A-Z]* doesn't work + # on (some?) SL6 systems + for group in [ABCDEFGHIJKLMNOPQRSTUVWXYZ]*; do + get_group_depth "${group}" + done + }; + done +} + +# re-scan available groups. +# +# Note: +# Removing groups is not supported for the time being. Be aware, that +# a user might have a module loaded from this group. This cannot be checked. +# +# $1: root of modulefile hierarchy +rescan_groups() { + local -r roots="$@" + local root + for root in "${roots[@]}"; do + { + cd "${root}" + # for some unknown reason [A-Z]* doesn't work with + # some bash versions + for group in [ABCDEFGHIJKLMNOPQRSTUVWXYZ]*; do + if [[ -z "${GroupDepths[${group}]}" ]]; then + get_group_depth "${group}" + fi + 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' + + 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 ! is_used_group "${_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 ! is_used_release $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 () { + declare -g UsedGroups + declare -g MODULEPATH + + local dirs_to_add=() + while (( $# > 0)); do + arg="$1" + # if is release + # ... + # elif is group + # ... + # elif matches modulepath root + # ... + # elif is directory + # ... + # else + # error + # + if is_release "${arg}"; then + # releases are always *appended* + std::append_path UsedReleases "${arg}" + elif [[ "${arg}" =~ "flag=" ]]; then + std::append_path UseFlags "${arg/flag=}" + elif [[ "${arg}" =~ "overlay=" ]]; then + local overlay="${arg/overlay=}" + [[ -d "${overlay}" ]] || \ + std:die 3 "%s %s: is not a directory -- %s\n" \ + "${CMD}" "${FUNCNAME[0]##*_}" "${overlay}" + if [[ ! ${Overlays[${overlay}]} ]]; then + Overlays[${overlay}]=1 + PMODULES_OVERLAYS+=:${overlay} + get_group_depths "${!Overlays[@]}" + for group in ${UsedGroups//:/ }; do + local dir="${overlay}/${group}/${PMODULES_MODULEFILES_DIR}" + if [[ -d "${dir}" ]]; then + std::prepend_path MODULEPATH "${dir}" + fi + done + fi + + elif [[ ! ${arg} =~ */* ]] && is_group "${arg}"; then + if (( ${GroupDepths[$arg]} != 0 )); then + std::die 3 "%s %s: cannot add group to module path -- %s\n" \ + "${CMD}" "${FUNCNAME[0]##*_}" "${arg}" + fi + std::append_path UsedGroups "${arg}" + 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 + elif [[ ${arg} =~ ^${PMODULES_ROOT} ]]; then + std::die 3 "%s %s: illegal directory -- %s\n" \ + "${CMD}" "${FUNCNAME[0]##*_}" "${arg}" + elif [[ -d ${arg} ]]; then + ${add2path_func} MODULEPATH "$(cd "${arg}" && pwd)" + else + std::die 3 "%s %s: neither a directory, release or group -- %s\n" \ + "${CMD}" "${FUNCNAME[0]##*_}" "${arg}" + fi + shift + done + + export_env ${g_shell} MODULEPATH UsedGroups + } + + 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 + else + use "${args[@]}" + fi +} + +############################################################################## +# +# 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 dirs_to_remove=() + 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: missing argument\n" \ + "${CMD}" "${subcommand}" + fi + + local arg + for arg in "${args[@]}"; do + # if is release + # ... + # elif is group + # ... + # elif matches modulepath root + # ... + # elif is directory + # ... + local modulefiles_dir="${PMODULES_ROOT}/${arg}/${PMODULES_MODULEFILES_DIR}" + if is_release "${arg}"; then + std::remove_path UsedReleases "${arg}" + + elif [[ "${arg}" =~ "flag=" ]]; then + std::remove_path UseFlags "${arg/flag=}" + + elif [[ "${arg}" =~ "overlay=" ]]; then + local overlay="${arg/overlay=}" + [[ -d "${overlay}" ]] || \ + std::die 3 "%s %s: is not a directory -- %s\n" \ + "${CMD}" "${FUNCNAME[0]##*_}" "${overlay}" + [[ "${overlay}" == "${PMODULES_ROOT}" ]] && \ + std::die 3 "%s %s: cannot remove root overlay -- %s\n" \ + "${CMD}" "${FUNCNAME[0]##*_}" "${overlay}" + if [[ ${Overlays[${overlay}]} ]]; then + [[ "${_LMFILES_}" =~ "${overlay}" ]] && \ + std::die 3 "%s %s: cannot remove overlay -- %s\n" \ + "${CMD}" "${FUNCNAME[0]##*_}" "${overlay}" + unset Overlays[${overlay}] + std::remove_path PMODULES_OVERLAYS "${overlay}" + for dir in "${modulepath[@]}"; do + if [[ "${dir}" =~ "${overlay}" ]]; then + std::remove_path MODULEPATH "${dir}" + fi + done + fi + + elif [[ ! ${arg} =~ */* ]] && [[ -d ${modulefiles_dir} ]]; then + if (( ${GroupDepths[$arg]} != 0 )); then + std::die 3 "%s %s: %s" \ + "${CMD}" "${FUNCNAME[0]##*_}" \ + "cannot remove group from module path -- %s\n" \ + "${arg}" + fi + std::remove_path UsedGroups "${arg}" + dirs_to_remove+=( ${modulefiles_dir} ) + + elif [[ -d ${arg} ]]; then + local normalized_dir=$(cd "${arg}" && pwd) + dirs_to_remove+=( ${normalized_dir} ) + elif [[ ${arg} =~ ^${PMODULES_ROOT} ]]; then + std::die 3 "%s %s: illegal directory -- %s\n." \ + "${CMD}" "${FUNCNAME[0]##*_}" "${arg}" + else + std::die 3 "%s %s: not a directory -- %s\n" \ + "${CMD}" "${FUNCNAME[0]##*_}" "${arg}" + fi + shift + done + + declare -g UsedGroups + export_env ${g_shell} UsedGroups + [[ ${#dirs_to_remove[@]} == 0 ]] && return + for dir in "${dirs_to_remove[@]}"; do + "${modulecmd}" "${g_shell}" "${subcommand}" "${dir}" + done +} + +############################################################################## +# +# 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 + for root in "${!Overlays[@]}"; do + for group in ${PMODULES_DEFAULT_GROUPS}; do + local dir="${root}/${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 +} + +reset_used_releases() { + declare -g UsedReleases='' + for r in ${PMODULES_DEFAULT_RELEASES//:/ }; do + std::append_path UsedReleases "${r}" + done +} + +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 -g UseFlags=() + reset_modulepath + reset_used_groups + reset_used_releases + init_path + init_manpath + export_env "${g_shell}" \ + 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: no arguments allowd\n" \ + "${CMD}" "${subcommand}" + fi + "${modulecmd}" "${g_shell}" "${subcommand}" + reset_modulepath + reset_used_groups + export_env ${g_shell} MODULEPATH UsedGroups +} + +############################################################################## +# +# 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: no arguments allowd\n" \ + "${CMD}" "${subcommand}" + fi + "${modulecmd}" "${g_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: no arguments allowed\n" \ + "${CMD}" "${subcommand}" + fi + pmodules_init + export_env ${g_shell} 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 + 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 + 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: illegal release name -- %s\n" \ + "${CMD}" 'search' "${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: illegal value for --with option -- %s\n" \ + "${CMD}" 'search' "${arg}" + fi + with_modules+=" && / ${arg//\//\\/}/" + ;; + -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=( "${!Overlays[@]}" ) + 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 + get_group_depths "${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}" "${g_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: two arguments required not less not more\n" \ + "${CMD}" "${subcommand}" + fi + "${modulecmd}" "${g_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 + bash | zsh ) + declare g_shell="$1" + ;; + csh | tcsh ) + declare g_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 + get_group_depths "${!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: