Commit 6eeb0a2f authored by gsell's avatar gsell
Browse files

scripts/Bootstrap/Pmodules/modulecmd.in:

- cache 'FAMILES' and 'HIERARCHY_DEPTHS'
- usage output reviewed
- append_path (), prepend_path(), remove_path() moved to libpmodules
- is_modulefile() added (testing shebang)
- argument/option handling reviewed, using 'getopt' now for all sub-commands
- in sub-command 'load': testing whether a module is loadable improved
- use 'info' for most output
- bugfixes
parent df9adc82
#!@PMODULES_HOME@/bin/bash
#
#set -o nounset
# we have to unset CDPATH, otherwise 'cd' prints the directoy!
unset CDPATH
declare -r PMODULES_DIR=$( cd "$(dirname $0)/.." && pwd )
#declare -r CMD=$(basename "$0")
declare -r CMD='module'
declare -r bindir=$(cd $(dirname "$0") && pwd)
declare -r prefix=$(dirname "${bindir}")
declare -r libdir="${prefix}/lib"
source "${libdir}/libpmodules.bash"
declare -r version='@PMODULES_VERSION@'
declare -r modulecmd="${PMODULES_DIR}/bin/modulecmd.tcl"
declare -r modulecmd="${bindir}/modulecmd.tcl"
declare -rx TCL_LIBRARY="${PMODULES_DIR}/lib/tcl8.6"
declare -rx PSI_LIBMODULES="${PMODULES_DIR}/lib/libmodules.tcl"
declare -rx TCL_LIBRARY="${libdir}/tcl8.6"
declare -rx PSI_LIBMODULES="${libdir}/libmodules.tcl"
declare -r modulepath_root="${PSI_PREFIX}/${PSI_MODULES_ROOT}"
# :FIXME: this is not save, if a component contains spaces.
declare -ra modulepath=( ${MODULEPATH//:/ } )
source "${PMODULES_DIR}/lib/libpmodules.bash"
declare verbosity_lvl=${PMODULES_VERBOSITY:-'verbose'}
if set -o | grep 'xtrace' | grep -q 'on'; then
declare -r __XTRACE__='on'
else
declare -r __XTRACE__='off'
fi
shopt -s nullglob
declare -a FAMILIES=()
declare -A HIERARCHY_DEPTHS
declare output_function='human_readable_output'
declare verbosity='silent'
declare userlvl=${PMODULES_USERLVL:-'novice'}
declare sw_force='no'
declare sw_create='no'
declare ignore_case=''
save_env() {
local s=''
while (( $# > 0 )); do
s+="$( typeset -p $1 );"
shift
done
echo export PMODULES_ENV=$( base64 <<< "$s" )
}
trap 'save_env FAMILIES HIERARCHY_DEPTHS' EXIT
print_version() {
echo "
......@@ -42,21 +52,14 @@ Copyright GNU GPL v2
usage() {
print_version
echo "
Usage: module [ switches ] [ subcommand ] [subcommand-args ]
USAGE:
module [ switches ] [ subcommand ] [subcommand-args ]
Switches:
-H|--help this usage info
SWITCHES:
-h|-H|-?|--help this usage info
-V|--version modules version & configuration options
-f|--force force active dependency resolution
-t|--terse terse format avail and list format
-l|--long long format avail and list format
-h|--human readable format avail and list format
-v|--verbose enable verbose messages
-s|--silent disable verbose messages
-c|--create create caches for avail and apropos [not yet implemented]
-i|--icase ignored
-u|--userlvl <lvl> set user level to (nov[ice],exp[ert],adv[anced])
Available SubCommands and Args:
SUBCOMMANDS:
+ add|load [switches ] modulefile [modulefile ...]
+ rm|unload modulefile [modulefile ...]
+ switch|swap [modulefile1] modulefile2
......@@ -65,7 +68,6 @@ Available SubCommands and Args:
+ search [ switches ] [ args ]
+ use [ switches ] [dir|family|release ...]
+ unuse dir|family|release [dir|family|release ...]
+ update
+ refresh
+ purge
+ list [ switches ]
......@@ -80,19 +82,20 @@ Available SubCommands and Args:
+ initlist
+ initclear
" 1>&2
die 1
}
subcommand_help_add() {
echo "
add modulefile...
load modulefile...
USAGE:
module add modulefile...
module load modulefile...
Load modulefile(s) into the shell environment. Loading a
'family-head' will extend the MODULEPATH. E.g.: loading a
compiler makes additional modules like openmpi and libraries
compiled with this compiler available.
" 1>&2
die 1
}
subcommand_help_load() {
......@@ -101,11 +104,13 @@ subcommand_help_load() {
subcommand_help_rm() {
echo "
rm modulefile...
unload modulefile...
USAGE:
module rm modulefile...
moudle unload modulefile...
Remove modulefile(s) from the shell environment. Removing
a 'family-head' will also unload all modules in the family.
" 1>&2
die 1
}
subcommand_help_unload() {
......@@ -114,12 +119,14 @@ subcommand_help_unload() {
subcommand_help_switch() {
echo "
switch [modulefile1] modulefile2
swap [modulefile1] modulefile2
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.
" 1>&2
die 1
}
subcommand_help_swap() {
......@@ -128,14 +135,16 @@ subcommand_help_swap() {
subcommand_help_display() {
echo "
display modulefile...
show modulefile...
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.
" 1>&2
die 1
}
subcommand_help_show() {
......@@ -144,12 +153,14 @@ subcommand_help_show() {
subcommand_help_apropos() {
echo "
apropos string
keyword string Seeks through the 'whatis' informations of all modulefiles for
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.
" 1>&2
die 1
}
subcommand_help_keyword() {
......@@ -159,7 +170,9 @@ subcommand_help_keyword() {
subcommand_help_avail() {
echo "
avail string List all available modulefiles in the current MODULEPATH. If
USAGE:
module avail 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.
......@@ -168,26 +181,28 @@ avail string List all available modulefiles in the current MODULEPATH. If
available modules may change either by loading other modules,
e.g. a compiler, or with the sub-command 'use'.
" 1>&2
die 1
}
subcommand_help_search() {
echo "
search [switches] STRING...
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.
--no-header Suppress output of a header.
--release=RELEASE
--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
-a|--all-releases
Search within all releases.
--with=STRING
--with=STRING
Search for modules compiled with modules matching string. The
command
......@@ -195,11 +210,13 @@ SWITCHES:
lists all modules in the hierarchy compiled with gcc 4.8.3.
" 1>&2
die 1
}
subcommand_help_use() {
echo "
use [-a|--append|-p|--prepend] [directory|family|release...]
USAGE:
module use [-a|--append|-p|--prepend] [directory|family|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
......@@ -213,8 +230,14 @@ use [-a|--append|-p|--prepend] [directory|family|release...]
be made available.
With a release as argument, this modules with this release
will be made available.
will be made available.
SWITCHES:
-a | --append -p | --prepend )
Append/prepend agrument to module search path or list of to be
searched releases.
" 1>&2
die 1
}
subcommand_help_unuse() {
......@@ -223,53 +246,72 @@ unuse directory|family|release...
Remove the given directory, family or release from the search
path.
" 1>&2
die 1
}
subcommand_help_update() {
echo "
update Attempt to reload all loaded modulefiles.
USAGE:
module update
Attempt to reload all loaded modulefiles.
" 1>&2
die 1
}
subcommand_help_refresh() {
echo "
refresh Force a refresh of all non-persistent components of currently
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.
" 1>&2
die 1
}
subcommand_help_purge() {
echo "
purge Unload all loaded modulefiles.
USAGE:
module purge
Unload all loaded modulefiles.
" 1>&2
die 1
}
subcommand_help_list() {
echo "
list List loaded modules.
USAGE:
module list
List loaded modules.
" 1>&2
die 1
}
subcommand_help_clear() {
echo "
clear Force the Modules package to believe that no modules are
USAGE:
module clear
Force the Modules package to believe that no modules are
currently loaded.
" 1>&2
die 1
}
subcommand_help_whatis() {
echo "
whatis [modulefile...]
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.
" 1>&2
die 1
}
subcommand_help_initadd() {
echo "
initadd modulefile...
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:
......@@ -292,79 +334,53 @@ initadd modulefile...
line is found in multiple shell initialization files, all
of the lines are changed.
" 1>&2
die 1
}
subcommand_help_initprepend() {
echo "
initprepend modulefile...
USAGE:
module initprepend modulefile...
Does the same as initadd but prepends the given modules to
the beginning of the list.
" 1>&2
die 1
}
subcommand_help_initrm() {
echo "
initrm modulefile...
USAGE:
module initrm modulefile...
Remove modulefile(s) from the shell's initialization files.
" 1>&2
die 1
}
subcommand_help_initswitch() {
echo "
initswitch modulefile1 modulefile2
USAGE:
module initswitch modulefile1 modulefile2
Switch modulefile1 with modulefile2 in the shell's initialization files.
" 1>&2
die 1
}
subcommand_help_initlist() {
echo "
initlist List all of the modulefiles loaded from the shell's initialization file.
USAGE:
module initlist
List all of the modulefiles loaded from the shell's initialization file.
" 1>&2
die 1
}
subcommand_help_initclear() {
echo "
initclear Clear all of the modulefiles from the shell's initialization files.
USAGE:
module initclear
Clear all of the modulefiles from the shell's initialization files.
" 1>&2
}
append_path () {
local -r P=$1
local -r d=$2
if ! echo ${!P} | egrep -q "(^|:)${d}($|:)" ; then
if [[ -z ${!P} ]]; then
eval $P=${d}
else
eval $P=${!P}:${d}
fi
fi
}
prepend_path () {
local -r P=$1
local -r d=$2
if ! echo ${!P} | egrep -q "(^|:)${d}($|:)" ; then
if [[ -z ${!P} ]]; then
eval $P=${d}
else
eval $P=${d}:${!P}
fi
fi
}
remove_path() {
local -r P=$1
local -r d=$2
local new_path=''
local -r _P=( ${!P//:/ } )
# loop over all entries in path
for entry in "${_P[@]}"; do
[[ "${entry}" != "${d}" ]] && new_path+=":${entry}"
done
# remove leading ':'
eval ${P}="${new_path:1}"
die 1
}
#
......@@ -424,56 +440,130 @@ 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}" == "#%Module1.0" ]]
}
subcommand_generic0() {
local -r subcommand=$1
shift
if [[ $# != 0 ]]; then
echo "${subcommand}: no arguments allowed" 1>&2
return 3
fi
local opts=''
opts=$(get_options -- '' "$@") || subcommand_help_${subcommand}
eval set -- "${opts}"
while (( $# > 0 )); do
case $1 in
-- )
shift
;;
* )
die 3 "${CMD} ${subcommand}: illegal argument -- $1"
;;
esac
done
"${modulecmd}" "${shell}" "${subcommand}"
}
subcommand_generic0plus() {
subcommand_generic1() {
local -r subcommand=$1
shift
"${modulecmd}" "${shell}" "${subcommand}" "$@"
local opts=''
opts=$(get_options -- '' "$@") || subcommand_help_${subcommand}
eval set -- "${opts}"
local args=()
while (( $# > 0 )); do
case $1 in
-- )
shift
;;
* )
if (( ${#args[@]} == 0 )); then
args+=( "$1" )
else
die 3 "${CMD} ${subcommand}: only one argument allowed"
fi
;;
esac
done
if (( ${#args[@]} == 0 )); then
die 3 "${CMD} ${subcommand}: missing argument"
fi
"${modulecmd}" "${shell}" "${subcommand}" "${args[@]}"
}
subcommand_generic1() {
subcommand_generic1plus() {
local -r subcommand=$1
shift
if [[ $# != 1 ]]; then
echo "${subcommand}: only one argument allowed" 1>&2
return 3
local opts=''
opts=$(get_options -- '' "$@") || subcommand_help_${subcommand}
eval set -- "${opts}"
local args=()
while (( $# > 0 )); do
case $1 in
-- )
shift
;;
* )
args+=( "$1" )
;;
esac
done
if (( ${#args[@]} == 0 )); then
die 3 "${CMD} ${subcommand}: missing argument"
fi
"${modulecmd}" "${shell}" "${subcommand}" "$1"
"${modulecmd}" "${shell}" "${subcommand}" "${args[@]}"
}
subcommand_generic1plus() {
subcommand_generic1or2() {
local -r subcommand=$1
shift
if [[ $# == 0 ]]; then
echo "${subcommand}: missing argument" 1>&2
return 3
local opts=''
opts=$(get_options -- '' "$@") || subcommand_help_${subcommand}
eval set -- "${opts}"
local args=()
while (( $# > 0 )); do
case $1 in
-- )
shift
;;
* )
if (( ${#args[@]} < 2 )); then
args+=( "$1" )
else
die 3 "${CMD} ${subcommand}: only one or two arguments are allowed"
fi
;;
esac
done
if (( ${#args[@]} == 0 )); then
die 3 "${CMD} ${subcommand}: missing argument"
fi
"${modulecmd}" "${shell}" "${subcommand}" "$@"
"${modulecmd}" "${shell}" "${subcommand}" "${args[@]}"
}
#
# load module
# load [-fsvw] <module>
#
# $1: module to load
#
subcommand_load() {
local release='unstable'
local release='undef'
local moduledir=''
local m=''
#
# Test whether a given module can be loaded according to the
# accepted releases.
#
# Note:
# The variable 'release' of the parent function will be set.
# Notes:
# The variable 'release' in function 'subcommand_load()' will be set.
# The release of a modulefile outsite our hierarchy is 'stable'.
#
# $1: absolute name of modulefile
#
......@@ -485,30 +575,68 @@ subcommand_load() {
#
# Test whether a given module is available.
# :FIXME: Check module shebang?
# Possible cases:
# - absolute file- or link-name in- or outside our hierarchy
# - relative file- or link-name in- or outside out hierarchy
# - full module name in- or outside our hierarchy
# - module name without version 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: is a loadable module
# 1: nothing found
# 2: wrong shebang
# 3: has unused release
# 4: inside our hierarchy but not loadable
#
# Notes:
# $1: module name
# The variable 'release' in function 'subcommand_load()' will be set.
# The variable 'm' in function 'subcommand_load()' may be set.
#
module_is_available() {
# return OK, if this is a file
# :FIXME: more checks are required if $1 is in ${PSI_PREFIX}!
#
[[ -f $1 ]] && return 0
is_available() {
local -r m=$1
# handle the case of an absolute or relative file- or link-name
if [[ -f ${m} ]]; then
if [[ "${m:0:1}" != "/" ]]; then
# convert to absolte path if relative
m=$(get_abspath "${m}")
fi
is_modulefile "${m}" || return 2
is_loadable "${m}" || return 3
if [[ "${m}" =~ "${PSI_PREFIX}" ]]; then
for dir in "${modulepath[@]}"; do