diff --git a/Pmodules/modulecmd.bash.in b/Pmodules/modulecmd.bash.in index f93c364..ad7681e 100755 --- a/Pmodules/modulecmd.bash.in +++ b/Pmodules/modulecmd.bash.in @@ -77,7 +77,10 @@ export_env() { done } +declare g_env_must_be_saved='no' + save_env() { + [[ ${g_env_must_be_saved} == 'no' ]] && return 0 local -r shell="$1" shift local s='' @@ -97,7 +100,7 @@ trap 'save_env ${g_shell} GroupDepths UsedReleases UseFlags UsedGroups PMODULES_ # get release of module # Note: # - the release of a modulefile outside ${PMODULES_ROOT} is 'stable' -# - the release of a modulefile inside ${PMODULES_ROOT} without a +# - the release of a modulefile inside ${PMODULES_ROOT} without a # coresponding release file is 'unstable' # # Args: @@ -119,7 +122,7 @@ get_release() { # read releasefile, remove empty lines, spaces etc local -r data=$( < "${releasefile}" ) std::upvar $1 "${data}" - else + else std::upvar $1 'unstable' fi return 0 @@ -243,7 +246,7 @@ subcommand_generic1plus() { ############################################################################## # # load [-fsvw] -# +# # $1: module to load # Subcommands[add]='load' @@ -254,7 +257,7 @@ 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 + 'group-head' will extend the MODULEPATH. E.g.: loading a compiler makes additional modules like openmpi and libraries compiled with this compiler available. ' @@ -266,7 +269,7 @@ subcommand_load() { local prefix='' local m='' - local saved_IFS="${IFS}"; + local saved_IFS="${IFS}"; IFS=':' local -a modulepath=(${MODULEPATH}) IFS=${saved_IFS} @@ -308,8 +311,8 @@ subcommand_load() { # 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 + # 100% reliable: One of the Pmodules extensions must be + # called before something like # setenv FOO_PREFIX bar # can be used. # @@ -447,6 +450,7 @@ subcommand_load() { std::die 3 "%s %s: illegal release name -- %s\n" \ "${CMD}" 'load' "${release}" std::append_path UsedReleases "${release}" + g_env_must_be_saved='yes' fi fi local found='' @@ -484,7 +488,7 @@ subcommand_load() { # :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'*} @@ -695,14 +699,14 @@ USAGE: 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 + 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. @@ -720,7 +724,7 @@ subcommand_avail() { # use this variable in the output functions local -a mods=() local dir='' - + # get number of columns of terminal cols=$(tput cols) @@ -857,7 +861,7 @@ subcommand_avail() { if (( ${#pattern[@]} == 0 )); then pattern+=( '' ) fi - local saved_IFS=${IFS}; + local saved_IFS=${IFS}; IFS=':' local -a modulepath=(${MODULEPATH}) IFS=${saved_IFS} @@ -888,7 +892,8 @@ get_group_depth () { # instead of 0 # :FIXME: better solution? (( depth == -4 )) && (( depth = 0 )) - GroupDepths[$group]=${depth} + GroupDepths[$group]=${depth} + g_env_must_be_saved='yes' } # @@ -908,14 +913,14 @@ get_group_depths () { # re-scan available groups. # # Note: -# Removing groups is not supported for the time being. Be aware, that +# 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 root="$1" { - cd "${root}" + 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 @@ -943,7 +948,7 @@ USAGE: 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 + With a group as argument, the modules in this group will be made available. With a release as argument, this modules with this release @@ -957,7 +962,7 @@ SWITCHES: subcommand_use() { local -r subcommand='use' - local saved_IFS=${IFS}; + local saved_IFS=${IFS}; IFS=':' local -a modulepath=(${MODULEPATH}) IFS=${saved_IFS} @@ -978,7 +983,7 @@ subcommand_use() { std::info "\t${_group}\n" fi done - + std::info "\nUsed releases:\n" for r in ${UsedReleases//:/ }; do std::info "\t${r}\n" @@ -1010,12 +1015,8 @@ subcommand_use() { } use () { - declare -g UsedGroups - declare -g MODULEPATH - - local dirs_to_add=() while (( $# > 0)); do - arg="$1" + local arg="$1" # if is release # ... # elif is group @@ -1031,28 +1032,39 @@ subcommand_use() { 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} =~ */* ]] && [[ -d ${modulefiles_dir} ]]; then if (( ${GroupDepths[$arg]} != 0 )); then - std::die 3 "%s %s: cannot add group to module path -- %s\n" \ - "${CMD}" "${FUNCNAME[0]##*_}" "${arg}" + std::die 3 "%s %s: %s -- %s\n" \ + "${CMD}" "${subcommand}" \ + "cannot add group to module path" \ + "${arg}" fi std::append_path UsedGroups "${arg}" - dirs_to_add+=( ${modulefiles_dir} ) + ${add2path_func} MODULEPATH "${modulefiles_dir}" + elif [[ ${arg} =~ ^${PMODULES_ROOT} ]]; then - std::die 3 "%s %s: illegal directory -- %s\n" \ - "${CMD}" "${FUNCNAME[0]##*_}" "${arg}" + std::die 3 "%s %s: %s -- %s\n" \ + "${CMD}" "${subcommand}" \ + "illegal directory" \ + "${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}" + std::die 3 "%s %s: %s -- %s\n" \ + "${CMD}" "${subcommand}" \ + "neither a directory, release or group" \ + "${arg}" fi shift done - - export_env ${g_shell} MODULEPATH UsedGroups + g_env_must_be_saved='yes' + export_env ${g_shell} 'MODULEPATH' } local -a args=() @@ -1097,7 +1109,62 @@ unuse directory|group|release... subcommand_unuse() { local -r subcommand='unuse' - local dirs_to_remove=() + unuse() { + while (( $# > 0 )); do + local arg=$1 + local modulefiles_dir="${PMODULES_ROOT}/${arg}/${PMODULES_MODULEFILES_DIR}" + if is_release "${arg}"; then + # argument is release + std::remove_path UsedReleases "${arg}" + + elif [[ "${arg}" =~ "flag=" ]]; then + # argument is flag + std::remove_path UseFlags "${arg/flag=}" + + elif [[ ! ${arg} =~ */* ]] && [[ -d ${modulefiles_dir} ]]; then + # argument is group + if (( ${GroupDepths[$arg]} != 0 )); then + std::die 3 "%s %s: %s -- %s\n" \ + "${CMD}" "${subcommand}" \ + "cannot remove group" \ + "${arg}" + fi + 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}" + std::remove_path MODULEPATH "${modulefiles_dir}" + + elif [[ -d ${arg} ]]; then + # argument is a modulepath + local normalized_dir=$(cd "${arg}" && pwd) + std::remove_path MODULEPATH "${normalized_dir}" + + elif [[ ${arg} =~ ^${PMODULES_ROOT} ]]; then + # argument looks like a group in our root + std::die 3 "%s %s: %s -- %s\n." \ + "${CMD}" "${subcommand}" \ + "illegal directory" \ + "${arg}" + + else + # oops + std::die 3 "%s %s: %s -- %s\n" \ + "${CMD}" "${subcommand}" \ + "not a valid argument" \ + "${arg}" + + fi + shift + done + g_env_must_be_saved='yes' + export_env "${g_shell}" 'MODULEPATH' + } + local -a args=() while (( $# > 0)); do case "$1" in @@ -1113,53 +1180,11 @@ subcommand_unuse() { shift done if (( ${#args[@]} == 0 )); then - std::die 3 "%s %s: missing argument\n" \ - "${CMD}" "${subcommand}" + std::die 3 "%s %s: %s\n" \ + "${CMD}" "${subcommand}" \ + 'missing argument' 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} =~ */* ]] && [[ -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 + unuse "${args[@]}" } ############################################################################## @@ -1215,6 +1240,7 @@ reset_used_groups() { for group in ${PMODULES_DEFAULT_GROUPS}; do std::append_path UsedGroups "${group}" done + g_env_must_be_saved='yes' } reset_used_releases() { @@ -1222,6 +1248,7 @@ reset_used_releases() { for r in ${PMODULES_DEFAULT_RELEASES//:/ }; do std::append_path UsedReleases "${r}" done + g_env_must_be_saved='yes' } init_path() { @@ -1304,7 +1331,7 @@ subcommand_purge() { "${modulecmd}" "${g_shell}" "${subcommand}" reset_modulepath reset_used_groups - export_env ${g_shell} MODULEPATH UsedGroups + export_env ${g_shell} MODULEPATH } ############################################################################## @@ -1404,7 +1431,7 @@ USAGE: Search installed modules. If an argument is given, search for modules whose name match the argument. -SWITCHES: +SWITCHES: --no-header Suppress output of a header. @@ -1415,7 +1442,7 @@ SWITCHES: -a|--all-releases Search within all releases. - + --with=STRING Search for modules compiled with modules matching string. The command @@ -1532,7 +1559,7 @@ subcommand_search() { print_result "${tmpfile}" rm -f "${tmpfile}" } - + while (( $# > 0 )); do case $1 in -H | --help ) @@ -1597,11 +1624,11 @@ subcommand_search() { if [[ -z "${src_prefix}" ]]; then src_prefix="${PMODULES_ROOT}" fi - + if [[ "${opt_use_releases}" == ":" ]]; then opt_use_releases=":${UsedReleases}:" fi - + if [[ ${#modules[@]} == 0 ]]; then modules+=( '' ) fi @@ -1754,7 +1781,7 @@ USAGE: .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 + 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