commit 713762d4ff851078565aa8b0f735da9999d9b872 Author: Achim Gsell Date: Thu Sep 17 15:03:03 2015 +0200 - Bootstrap/ moved one level up - pmodules.xyz() functions renamed to pbuild::xyz() - use pbuild for bootstrapping components diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6c96710 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +environment.bash +modulecmd.bash +modmanage.bash diff --git a/Modules/build b/Modules/build new file mode 100755 index 0000000..c2cb49c --- /dev/null +++ b/Modules/build @@ -0,0 +1,55 @@ +#!/usr/bin/env pbuild + +TCL_DIR="${PMODULES_ROOT}/Tools/Pmodules/${PMODULES_VERSION}" + +PATH="${TCL_DIR}/bin:${PATH}" + +pmodules.configure() { + case ${OS} in + Linux ) + declare -x LIBS="-lz -lpthread" + ;; + Darwin ) + declare -x LIBS="-lz -framework CoreFoundation" + ;; + esac + CPPFLAGS="-DUSE_INTERP_ERRORLINE" "${MODULE_SRCDIR}"/configure \ + --prefix="${PREFIX}" \ + --exec-prefix="${PREFIX}" \ + --with-module-path="${PMODULES_ROOT}/Tools/${PMODULES_MODULEFILES_DIR}" \ + --with-tcl="${TCL_DIR}/lib" \ + --without-x \ + --disable-versioning \ + || exit 1 +} + +pmodules.post_install() { + rm -v ${PREFIX}/Modules/bin/add.modules + rm -v ${PREFIX}/Modules/bin/mkroot + rm -rfv ${PREFIX}/Modules/modulefiles + mv -v ${PREFIX}/Modules/share/man/man1/module.1 ${PREFIX}/share/man/man1 + mv -v ${PREFIX}/Modules/share/man/man4/modulefile.4 ${PREFIX}/share/man/man4 + rmdir ${PREFIX}/Modules/bin + rmdir ${PREFIX}/Modules/share/man/man1 + rmdir ${PREFIX}/Modules/share/man/man4 + rmdir ${PREFIX}/Modules/share/man + rmdir ${PREFIX}/Modules/share + rmdir ${PREFIX}/Modules +} + +# fake module command +module() { + : +} + +# use system gcc to compile +declare -rx CC=gcc + +pmodules.add_to_group 'Tools' +pmodules.make_all + +# Local Variables: +# mode: sh +# sh-basic-offset: 8 +# tab-width: 8 +# End: diff --git a/Pmodules/bash b/Pmodules/bash new file mode 100644 index 0000000..0683631 --- /dev/null +++ b/Pmodules/bash @@ -0,0 +1,155 @@ +#!/bin/bash + +############################################################################# +# bash 3 or newer ... +# +if [ ${BASH_VERSINFO:-0} -lt 3 ]; then + echo "BASH version ${BASH_VERSION} ist not supported! You need at least version 3..." + return +fi + +############################################################################# +# implement module comand as function +# +module() { + local -r modulecmd="${PMODULES_HOME}/bin/modulecmd" + + local -a args=() + local -a switches=() + + while (( $# > 0 ));do + case $1 in + -* ) + switches+=( $1 ) + ;; + [/~a-zA-Z]* ) + args+=( $1 ) + ;; + esac + shift + done + + [[ ${#args[@]} == 0 ]] && args+=( 'help' ) + [[ ${#args[@]} == 1 ]] && args+=( '--' ) + + local -i i=1 + for (( i=1; i < ${#args[@]}; i++ )); do + eval $( "${modulecmd}" bash "${args[0]}" "${switches[@]}" "${args[i]}" ) + done +} +export -f module + +############################################################################# +# helper functions +# +std::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 +} + +# +# Replace or remove a directory in a path variable. +# +# To remove a dir: +# std::replace_path PATH +# +# To replace a dir: +# std::replace_path PATH /replacement/path +# +# Args: +# $1 name of the shell variable to set (e.g. PATH) +# $2 a grep pattern identifying the element to be removed/replaced +# $3 the replacement string (use "" for removal) +# +# Based on solution published here: +# https://stackoverflow.com/questions/273909/how-do-i-manipulate-path-elements-in-shell-scripts +# +std::replace_path () { + local -r path=$1 + local -r removepat=$2 + local -r replacestr=$3 + + local -r removestr=$(echo "${!path}" | tr ":" "\n" | grep -m 1 "^$removepat\$") + export $path=$(echo "${!path}" | tr ":" "\n" | sed "s:^${removestr}\$:${replacestr}:" | + sed '/^\s*$/d' | tr "\n" ":" | sed -e 's|^:||' -e 's|:$||') +} + +save_env() { + local s='' + while (( $# > 0 )); do + s+="$( typeset -p $1 );" + shift + done + echo export PMODULES_ENV=$( "${PMODULES_HOME}/bin/base64" --wrap=0 <<< "$s" ) +} + +#module purge + +############################################################################# +# setup environment +# +declare -x LOADEDMODULES='' +declare -x _LMFILES_='' +declare -x PMODULES_USED_GROUPS='' +declare -x MODULEPATH='' +for group in ${PMODULES_DEFAULT_GROUPS//:/ }; do + std::append_path MODULEPATH "${PMODULES_ROOT}/${group}/${PMODULES_MODULEFILES_DIR}" + std::append_path PMODULES_USED_GROUPS "${group}" +done +declare -x UsedReleases='' +for r in ${PMODULES_DEFAULT_RELEASES//:/ }; do + std::append_path UsedReleases "${r}" +done + +eval $(save_env UsedReleases PMODULES_DEFAULT_RELEASES PMODULES_DEFAULT_GROUPS PMODULES_DEFINED_RELEASES) +unset UsedReleases +unset PMODULES_DEFAULT_RELEASES +unset PMODULES_DEFAULT_GROUPS +unset PMODULES_DEFINED_RELEASES + +std::replace_path PATH "${PMODULES_HOME%/*}/.*" +std::replace_path MANPATH "${PMODULES_HOME%/*}/.*" +std::append_path PATH "${PMODULES_HOME}/bin" + +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 + +############################################################################# +# initialize bash completion +# +if [[ -r "${PMODULES_HOME}/init/bash_completion" ]]; then + source "${PMODULES_HOME}/init/bash_completion" +fi + +############################################################################# +# legacy... +# +unset MODULE_VERSION +unset MODULE_VERSION_STACK +unset MODULESHOME + +# Local Variables: +# mode: sh +# sh-basic-offset: 8 +# tab-width: 8 +# End: diff --git a/Pmodules/bash_completion b/Pmodules/bash_completion new file mode 100644 index 0000000..76c789a --- /dev/null +++ b/Pmodules/bash_completion @@ -0,0 +1,76 @@ +# +# Bash commandline completion (bash 3.0 and above) for Modules 3.2.10 +# +_module_avail() { + "${PMODULES_HOME}"/bin/modulecmd bash -t avail 2>&1 | sed ' + /:$/d; + /:ERROR:/d; + s#^\(.*\)/\(.\+\)(default)#\1\n\1\/\2#; + s#/(default)##g; + s#/*$##g;' +} + +_module_not_yet_loaded() { + comm -23 <(_module_avail|sort) <(tr : '\n' <<<${LOADEDMODULES}|sort) +} + +_module_long_arg_list() { + local cur="$1" i + + if [[ ${COMP_WORDS[COMP_CWORD-2]} == sw* ]] + then + COMPREPLY=( $(compgen -W "$(_module_not_yet_loaded)" -- "$cur") ) + return + fi + for ((i = COMP_CWORD - 1; i > 0; i--)) + do case ${COMP_WORDS[$i]} in + add|load) + COMPREPLY=( $(compgen -W "$(_module_not_yet_loaded)" -- "$cur") ) + break;; + rm|remove|unload|switch|swap) + COMPREPLY=( $(IFS=: compgen -W "${LOADEDMODULES}" -- "$cur") ) + break;; + esac + done +} + +_module() { + local cur="$2" prev="$3" cmds opts + + COMPREPLY=() + + cmds="add apropos avail clear dependencies display help\ + initadd initclear initlist initprepend initrm initswitch\ + keyword list load purge refresh rm search show swap switch sync\ + unload unuse update use whatis" + + opts="-c -f -h -i -l -s -t -u -v -H -V\ + --create --force --help --human --icase\ + --long --silent --terse --userlvl --verbose --version" + + case "$prev" in + add|load) COMPREPLY=( $(compgen -W "$(_module_not_yet_loaded)" -- "$cur") );; + rm|remove|unload|switch|swap) + COMPREPLY=( $(IFS=: compgen -W "${LOADEDMODULES}" -- "$cur") );; + unuse) COMPREPLY=( $(IFS=: compgen -W "${MODULEPATH}" -- "$cur") );; + use|*-a*) ;; # let readline handle the completion + -u|--userlvl) COMPREPLY=( $(compgen -W "novice expert advanced" -- "$cur") );; + display|help|show|whatis) + COMPREPLY=( $(compgen -W "$(_module_avail)" -- "$cur") );; + *) if test $COMP_CWORD -gt 2 + then + _module_long_arg_list "$cur" + else + case "$cur" in + # The mappings below are optional abbreviations for convenience + ls) COMPREPLY="list";; # map ls -> list + r*) COMPREPLY="rm";; # also covers 'remove' + sw*) COMPREPLY="switch";; + + -*) COMPREPLY=( $(compgen -W "$opts" -- "$cur") );; + *) COMPREPLY=( $(compgen -W "$cmds" -- "$cur") );; + esac + fi;; + esac +} +complete -o default -F _module module diff --git a/Pmodules/csh b/Pmodules/csh new file mode 100644 index 0000000..93c2d64 --- /dev/null +++ b/Pmodules/csh @@ -0,0 +1,44 @@ +if ($?tcsh) then + set modules_shell="tcsh" +else + set modules_shell="csh" +endif +set exec_prefix = $PMODULES_HOME/bin' + +set prefix="" +set postfix="" + +if ( $?histchars ) then + set histchar = `echo $histchars | cut -c1` + set _histchars = $histchars + + set prefix = 'unset histchars;' + set postfix = 'set histchars = $_histchars;' +else + set histchar = \! +endif + +if ($?prompt) then + set prefix = "$prefix"'set _prompt="$prompt";set prompt="";' + set postfix = "$postfix"'set prompt="$_prompt";unset _prompt;' +endif + +if ($?noglob) then + set prefix = "$prefix""set noglob;" + set postfix = "$postfix""unset noglob;" +endif +set postfix = "set _exit="'$status'"; $postfix; test 0 = "'$_exit;' + +alias module $prefix'eval `'$exec_prefix'/modulecmd '$modules_shell' '$histchar'*`; '$postfix +unset exec_prefix +unset prefix +unset postfix + + +if (! $?MODULEPATH ) then + setenv MODULEPATH `sed -n 's/[ #].*$//; /./H; $ { x; s/^\n//; s/\n/:/g; p; }' ${MODULESHOME}/init/.modulespath` +endif + +if (! $?LOADEDMODULES ) then + setenv LOADEDMODULES "" +endif diff --git a/Pmodules/dialog.bash b/Pmodules/dialog.bash new file mode 100755 index 0000000..161546f --- /dev/null +++ b/Pmodules/dialog.bash @@ -0,0 +1,353 @@ +#!/usr/bin/env bash + +# Hardcoded path to dialog software +DIALOG_CMD=$PMODULES_HOME/bin/dialog + +declare -a modlist # module info +declare -A selected # module info indices selected +declare -a depcnt # dependency reference counter by module info index +declare -A uidmap # unique module id to module info index +declare -A modmap # map module names to module info indices for modlist +declare -A fdmap # module name to family definition mapping +declare -A fmmap # module name to family member mapping +declare -a relmap # module info index to release mapping +declare tempfile # temporary dialog results + +set_difference() { # $1 \ $2 + local -a operand1=($1) + local -a operand2=($2) + local -A members + local -i elem + for elem in "${operand1[@]}"; do + members[$elem]=1 + done + for elem in "${operand2[@]}"; do + unset members[$elem] + done + echo ${!members[@]} +} + +set_merge() { # $1 U $2 (where $1 and $2 are disjoint) + if [[ -z "$1" ]]; then + echo "$2" + elif [[ -z "$2" ]]; then + echo "$1" + else + echo "$1 $2" + fi +} + +set_union() { # $1 U $2 (sorted) + local -a operand1=($1) + local -a operand2=($2) + local -A members + local -i elem + for elem in ${operand1[@]} ${operand2[@]}; do + members[$elem]=1 + done + { IFS=$'\n'; echo "${!members[*]}"; } | sort -n +} + +# unique id for a module +unique_id() { # $1: module info index + local -a minfo=( ${modlist[$1]} ) + if (( ${#minfo[@]} < 4 )); then + echo ${minfo[0]} + else + echo "${minfo[@]:3} ${minfo[0]}" + fi +} + +mod_path() { # $1: module info index + local -i i + local -a m=(${modlist[$1]}) + local res="$PMODULES_ROOT/${fmmap[${m[0]%%/*}]}/${m[0]}" + for (( i=${#m[@]}; i>3; i-- )); do + res+="/${m[i-1]}" + done + echo "$res" +} + +calc_deps() { # $1: module info index + local dpath="$(mod_path $1)/.dependencies" + [[ ! -r "$dpath" ]] && return + local -a d=( $(< "$dpath") ) # dependencies as versioned module names + local -A p # map family to versioned module name + local -A did # map dependency (versioned module name) to unique module id + local -a deps # set of module info indices + local m n f + for m in ${d[@]}; do + n=${m%%/*} + f=${fdmap[$n]} + [[ -n "$f" ]] && { p[$f]=$m; } + f=${fmmap[$n]} + if [[ -z "$f" ]]; then + did[$m]=$m + else + n=${p[$f]} + if [[ -z "$n" ]]; then + did[$m]=$m + else + did[$m]="${did[$n]} $m" + fi + fi + deps+=( ${uidmap["${did[$m]}"]} ) + done + echo "${deps[@]}" +} + +update_deps() { # $1: 1-add dependency, -1-remove dependency $2: set of module info indices + [[ -z "$2" ]] && return + local -a q=($2) # work queue + local deps="" # set of dependencies + local -i m + while (( ${#q[@]} > 0 )); do + m=${q[-1]} + unset q[-1] + d="$(calc_deps $m)" + [[ -z "$d" ]] && continue + d="$(set_difference "$d" "$deps")" + [[ -z "$d" ]] && continue + q+=($d) + deps="$(set_merge "$d" "$deps")" + done + for m in $deps; do + let depcnt[m]+=$1 + done +} + +# "$1": source module environment +find_modules() { + # construct modlist/modmap/uidmap/depcnt/fmmap/relmap arrays from module search output + local -a mc # module info components + local -i i=0 + local current="" + local name m uid + while read m; do + mc=($m) + [[ "${mc[2]}" == "Legacy" ]] && continue # filter out legacy stuff + name=${mc[0]%%/*} + if [[ "$current" != "$name" ]]; then + modmap[$name]="$i" + current=$name + else + modmap[$name]+=" $i" + fi + modlist[i]=$m + uid="$(unique_id $i)" + uidmap["$uid"]=$i + depcnt[i]=0 + [[ -z ${fmmap[$name]} ]] && { fmmap[$name]=${mc[2]}; } + relmap[i]=${mc[1]} + i+=1 + done < <(${PMODULES_HOME}/bin/modulecmd bash search --src="$1" --no-header -a 2>&1) +} + +# "$1": source module environment +find_families() { + # construct fdmap + local -a t # tcl file components + local l s n + while read l; do + s=${l%%:*} + s=${s%/*} + n=${s##*/} + if [[ -z "${fdmap[$n]}" ]]; then + t=( ${l##*:} ) + fdmap[$n]=${t[-1]//\"} + fi + done < <(grep -R set-family "$1/*/${PMODULES_MODULEFILES_DIR}") +} + +select_uid() { # $1: module uid + local -a uidc=($1) # uid components + local name=${uidc[-1]%%/*} # module name + local midx=${uidmap["$1"]} # module info index + [[ -z "$midx" ]] && return + selected[$name]="$(set_union "${selected[$name]}" "$midx")" + update_deps 1 "$midx" +} + +preselect() { # "$1": prefix for preselected modules + # module paths must not contain white space + [[ -z "$1" ]] && return + local -a mpc # module path components + local -i i + local uid n + pushd "$1/$PMODULES_MODULEFILES_DIR" > /dev/null || exit 1; + trap "popd" EXIT + + for m in $(find . -follow -type f); do + n=${m##*/} + [[ "${n:0:1}" == "." ]] && continue + uid="" + mpc=( ${m//\// } ) + for ((i=2; i<${#mpc[@]}-2; i+=2)); do + uid+="${mpc[i]}/${mpc[i+1]} " + done + uid+="${mpc[-2]}/${mpc[-1]}" + PMODULES_ROOT="$1" select_uid "$uid" + done + + popd + trap - EXIT +} + +is_dependency() { # $1: module name + local -a map=(${modmap[$1]}) + local -i m + for ((m=0; m<${#map[@]}; m++)); do + (( ${depcnt[${map[m]}]} > 0 )) && return 0 + done + return 1 +} + +dialog_1() { + local -a input + local marker + local m + for m in $(IFS=$'\n'; echo "${!modmap[*]}" | sort); do + marker="" + [[ -n ${selected[$m]} ]] && { marker+="*"; } + is_dependency $m && { marker+="+"; } + input+=($m "$marker$m") + done + + $DIALOG_CMD --ok-label 'Select' \ + --extra-button --extra-label 'Exit' \ + --no-tags \ + --menu Modules 50 80 50 "${input[@]}" 2>$tempfile + return $? +} + +module_id() { # $@: module info components + echo "$1 ${@:4}" +} + +module_release() { # $@: module info components + echo "$2" +} + +dialog_2() { # $1: module name + local -a map=(${modmap[$1]}) + local -a sel=(${selected[$1]}) + local -i j # mapping index + local -i k=0 # selection index + local -a input + local marker minfo rel m s + for (( j=0; j!=${#map[@]}; j++ )); do + minfo=${modlist[${map[j]}]} + m="$(module_id $minfo)" + rel=" ($(module_release $minfo))" + [[ $rel = " (stable)" ]] && { rel=""; } + [[ "${map[j]}" = "${sel[k]}" ]] && { s="on"; k+=1; } || { s="off"; } + (( ${depcnt[${map[j]}]} > 0 )) && { marker="+"; l+=1; } || { marker=""; } + input+=( ${map[j]} "$marker$m$rel" $s ) + done + + $DIALOG_CMD --extra-button --extra-label 'Clear' --no-tags --checklist Versions 80 90 80 "${input[@]}" 2>$tempfile + return $? +} + +# final dialog output +module_out() { # $1: module info index + local -a args=(${modlist[$1]}) + echo "${args[@]}" +} + +# "$1": prefix for preselected modules (destination module environment) +# "$2": prefix for selectable modules (source module environment) +module_picker() { + find_families "$2" + find_modules "$2" + preselect "$1" + + tempfile=$(mktemp ${TMPDIR:-/tmp}/msyncXXXXXX) || { + echo "Unable to create temporary file!" + exit 1 + } + trap "rm -f $tempfile" EXIT + + local -i level=1 + local -i operation=0 # 0: OK, 1: Cancel + local oldsel + local sel + local m + while (( level != 0 )); do + case $level in + 1) + dialog_1 + res=$? + case $res in + 0) #OK + sel=$(< $tempfile) + level=2 + ;; + 1) #Cancel + operation=1 + level=0 + ;; + 3|255) #ESC/Exit = Commit + for m in ${selected[@]}; do + depcnt[m]=1 + done + for ((m=0; m<${#depcnt[@]}; m++)); do + (( ${depcnt[m]} > 0 )) && module_out $m >&2 + done + level=0 + ;; + *) + echo "Unknown return value from dialog_1: $res" + exit 1 + ;; + esac + ;; + 2) + dialog_2 $sel + res=$? + case $res in + 0) #OK + oldsel=${selected[$sel]} # old selection + selected[$sel]=$(< $tempfile) # new selection + PMODULES_ROOT="$2" update_deps -1 "$(set_difference "$oldsel" "${selected[$sel]}")" # remove dependencies + PMODULES_ROOT="$2" update_deps 1 "$(set_difference "${selected[$sel]}" "$oldsel")" # add dependencies + level=1 + ;; + 1|255) #ESC/Cancel + level=1 + ;; + 3) #Clear + oldsel=${selected[$sel]} # old selection + selected[$sel]="" # new selection + update_deps -1 "$oldsel" # remove dependencies + level=1 + ;; + *) + echo "Unknown return value from dialog_2: $res" + exit 1 + ;; + esac + ;; + *) + echo "Unknown level: $level" + exit 1 + ;; + esac + done + + rm -f $tempfile + trap - EXIT + + return $operation +} + +# if DIALOG_LIB is NOT set, call module picker +[[ ${DIALOG_LIB:+"is_lib"} == "is_lib" ]] || { + if [[ -x ${PMODULES_HOME}/bin/modulecmd ]]; then + module_picker "${1:-$PMODULES_ROOT}" "${2:-/afs/psi.ch/sys/psi.x86_64_slp6}" + exit $? + else + echo "ERROR: module environment configuration: ${PMODULES_HOME}/bin/modulecmd is not an executable!" + exit 1 + fi +} diff --git a/Pmodules/environment.bash.in b/Pmodules/environment.bash.in new file mode 100644 index 0000000..6adb09f --- /dev/null +++ b/Pmodules/environment.bash.in @@ -0,0 +1,27 @@ +#!/bin/bash +# +# Notes: +# - PMODULES_ROOT is derived from the location of this file. +# - Some for PMODULES_CONFIG_DIR. +# - The Pmodules software must be installed in +# ${PMODULES_ROOT}/Tools/Pmodules/${PMODULES_VERSION} +# + +declare -x PMODULES_MODULEFILES_DIR='modulefiles' +declare -x PMODULES_TEMPLATES_DIR='templates' + +declare -x PMODULES_DEFAULT_GROUPS='Tools Programming' +declare -x PMODULES_DEFINED_RELEASES=':unstable:stable:deprecated:' +declare -x PMODULES_DEFAULT_RELEASES='stable' + +if [[ -z ${PMODULES_VERSION} ]]; then + declare -x PMODULES_VERSION="@PMODULES_VERSION@" +fi + +# +# DO NOT CHANGE THE FOLLOWING LINES! + +declare -x PMODULES_ROOT=$(cd $(dirname "${BASH_SOURCE}")/.. && pwd) +declare -x PMODULES_CONFIG_DIR=$(basename $(cd $(dirname "${BASH_SOURCE}") && pwd)) +declare -x PMODULES_HOME="${PMODULES_ROOT}/Tools/Pmodules/${PMODULES_VERSION}" +declare -x PMODULES_DIR="${PMODULES_HOME}" diff --git a/Pmodules/environment.csh.in b/Pmodules/environment.csh.in new file mode 100755 index 0000000..937771c --- /dev/null +++ b/Pmodules/environment.csh.in @@ -0,0 +1,17 @@ +#!/bin/tcsh + +setenv PMODULES_ROOT "/opt/psi" + +if ( ! $?PMODULES_VERSION ) then + setenv PMODULES_VERSION "@PMODULES_VERSION@" +endif +setenv PMODULES_HOME "$PMODULES_ROOT/Tools/Pmodules/$PMODULES_VERSION" +setenv PMODULES_DIR "${PMODULES_HOME}" + +setenv PMODULES_CONFIG_DIR 'config' +setenv PMODULES_MODULEFILES_DIR 'modulefiles' +setenv PMODULES_TEMPLATES_DIR 'templates' + +setenv PMODULES_DEFAULT_GROUPS 'Tools Programming' +setenv PMODULES_DEFINED_RELEASES ':unstable:stable:deprecated:' +setenv PMODULES_DEFAULT_RELEASES 'stable' diff --git a/Pmodules/libmodules.tcl b/Pmodules/libmodules.tcl new file mode 100644 index 0000000..fbc7aa2 --- /dev/null +++ b/Pmodules/libmodules.tcl @@ -0,0 +1,340 @@ +# +# :TODO: +# switch/swap +# unload modules if parent removed +# + +if {[info exists env(PMODULES_DEBUG)] && $env(PMODULES_DEBUG)} { + proc debug {msg} { + set level [expr [info level] -2] + set r [catch {info level ${level}} e] + if {$r} { + set caller "" + } else { + set caller [lindex [split [info level [expr [info level] - 3]]] 0] + } + puts -nonewline stderr "${caller}: " + puts stderr ${msg} + } +} else { + proc debug {msg} {} +} + +debug "loading libmodules" + +proc module-addgroup { group } { + global env + global name + global version + + debug $group + set Implementation [file join {*}$::implementation] + + set GROUP [string toupper $group] + regsub -- "-" ${GROUP} "_" GROUP + setenv ${GROUP} $name + setenv ${GROUP}_VERSION $version + + set ::${group} $name + set ::${group}_version $version + + if { [module-info mode load] } { + debug "mode is load" + + append-path MODULEPATH $::PmodulesRoot/$group/$::PmodulesModulfilesDir/$Implementation + append-path PMODULES_USED_GROUPS $group + debug "mode=load: new MODULEPATH=$env(MODULEPATH)" + debug "mode=load: new PMODULES_USED_GROUPS=$env(PMODULES_USED_GROUPS)" + } elseif { [module-info mode remove] } { + # remove orphan modules + debug "remove orphan modules" + set GROUP [string toupper $group] + if { [info exists env(PMODULES_LOADED_${GROUP})] } { + set modules [split $env(PMODULES_LOADED_${GROUP}) ":"] + foreach m ${modules} { + if { ${m} == "--APPMARKER--" } { + continue + } + if { [is-loaded ${module_name}] } { + debug "unloading module: $m" + module unload ${m} + } + } + } + remove-path MODULEPATH $::PmodulesRoot/$group/$::PmodulesModulfilesDir/$Implementation + remove-path PMODULES_USED_GROUPS $group + debug "mode=remove: $env(MODULEPATH)" + debug "mode=remove: $env(PMODULES_USED_GROUPS)" + } + if { [module-info mode switch2] } { + debug "mode=switch2" + append-path MODULEPATH $::PmodulesRoot/$group/$::PmodulesModulfilesDir/[module-info name] + append-path PMODULES_USED_GROUPS ${group} + } +} + +proc set-family { group } { + module-addgroup $group +} + +proc _pmodules_update_loaded_modules { group name version } { + if { ${group} == "--APPMARKER--" } { + return + } + set GROUP [string toupper $group] + debug "${GROUP} $name/$version" + append-path PMODULES_LOADED_${GROUP} "$name/$version" + remove-path PMODULES_LOADED_${GROUP} "--APPMARKER--" +} + +# +# load dependencies, but do *not* unload dependencies +# +proc _pmodules_load_dependencies { fname } { + if { ! [ file exists ${fname} ] } { + return + } + if { ! [module-info mode load] } { + return + } + debug "load dependencies from: ${fname}" + # Slurp up the data file + set fp [open ${fname} r] + set file_data [read ${fp}] + close ${fp} + set data [split ${file_data} "\n"] + foreach line ${data} { + debug "MODULEPATH=$::env(MODULEPATH)" + set module_name [string trim $line] + if { ${module_name} == "#" || ${module_name} == "" } { + continue + } + if { [is-loaded ${module_name}] } { + debug "module already loaded: ${module_name}" + continue + } + debug "module load: ${module_name}" + module load ${module_name} + } +} + +proc lreverse_n { list n } { + set res {} + set i [expr [llength $list] - $n] + while {$i >= 0} { + lappend res {*}[lrange $list $i [expr $i+$n-1]] + incr i -$n + } + set res +} + +# +# set standard environment variables +# +proc _pmodules_setenv { PREFIX name version } { + # + # Hack for supporting legacy modules + if { "${::group}" == "Legacy" } { + debug "this is a legacy module..." + return + } + + set NAME [string toupper $name] + regsub -- "-" ${NAME} "_" NAME + + if { ! [info exist ::dont-setenv] } { + set ::dont-setenv {} + } + + if { ${version} != "" } { + if { [lsearch ${::dont-setenv} "${NAME}_VERSION"] == -1 } { + setenv ${NAME}_VERSION $version + } + } + + if { [file isdirectory "$PREFIX"] } { + if { [lsearch ${::dont-setenv} "${NAME}_PREFIX"] == -1 } { + setenv ${NAME}_PREFIX $PREFIX + } + if { [lsearch ${::dont-setenv} "${NAME}_DIR"] == -1 } { + setenv ${NAME}_DIR $PREFIX + } + if { [lsearch ${::dont-setenv} "${NAME}_HOME"] == -1 } { + setenv ${NAME}_HOME $PREFIX + } + } else { + debug "$PREFIX is not a directory" + } + + if { [file isdirectory "$PREFIX/bin"] } { + if { [lsearch ${::dont-setenv} "PATH"] == -1 } { + prepend-path PATH $PREFIX/bin + } + } + + if { [file isdirectory "$PREFIX/sbin"] } { + if { [lsearch ${::dont-setenv} "PATH"] == -1 } { + prepend-path PATH $PREFIX/sbin + } + } + + if { [file isdirectory "$PREFIX/share/man"] } { + if { [lsearch ${::dont-setenv} "MANPATH"] == -1 } { + prepend-path MANPATH $PREFIX/share/man + } + } + + # set various environment variables - as long as they are not blacklisted + debug "prepend to include paths" + if { [file isdirectory "$PREFIX/include"] } { + if { [lsearch ${::dont-setenv} "C_INCLUDE_PATH"] == -1 } { + prepend-path C_INCLUDE_PATH $PREFIX/include + } + if { [lsearch ${::dont-setenv} "CPLUS_INCLUDE_PATH"] == -1 } { + prepend-path CPLUS_INCLUDE_PATH $PREFIX/include + } + if { [lsearch ${::dont-setenv} "${NAME}_INCLUDE_DIR"] == -1 } { + setenv ${NAME}_INCLUDE_DIR $PREFIX/include + } + } + + debug "prepend to library paths" + if { [file isdirectory "$PREFIX/lib"] } { + if { [lsearch ${::dont-setenv} "LIBRARY_PATH"] == -1 } { + prepend-path LIBRARY_PATH $PREFIX/lib + } + if { [lsearch ${::dont-setenv} "LD_LIBRARY_PATH"] == -1 } { + prepend-path LD_LIBRARY_PATH $PREFIX/lib + } + if { [lsearch ${::dont-setenv} "${NAME}_LIBRARY_DIR"] == -1 } { + setenv ${NAME}_LIBRARY_DIR $PREFIX/lib + } + } + + debug "prepend to library paths (64bit)" + if { [file isdirectory "$PREFIX/lib64"] } { + if { [lsearch ${::dont-setenv} "LIBRARY_PATH"] == -1 } { + prepend-path LIBRARY_PATH $PREFIX/lib64 + } + if { [lsearch ${::dont-setenv} "LD_LIBRARY_PATH"] == -1 } { + prepend-path LD_LIBRARY_PATH $PREFIX/lib64 + } + if { [lsearch ${::dont-setenv} "${NAME}_LIBRARY_DIR"] == -1 } { + setenv ${NAME}_LIBRARY_DIR $PREFIX/lib64 + } + } +} + +proc module-url { _url } { + set ::url ${_url} +} + +proc module-license { _license } { + set ::license ${_license} +} + +proc module-maintainer { _maintainer } { + set ::maintainer ${_maintainer} +} + +proc module-help { _help } { + set ::help ${_help} +} + +proc ModulesHelp { } { + if { [info exists ::whatis] } { + puts stderr "${::whatis}" + } else { + module whatis ModulesCurrentModulefile + } + if { [info exists ::version] } { + puts stderr "Version: ${::version}" + } else { + module whatis + } + if { [info exists ::url] } { + puts stderr "Homepage: ${::url}" + } + if { [info exists ::license] } { + puts stderr "License: ${::license}" + } + if { [info exists ::maintainer] } { + puts stderr "Maintainer: ${::maintainer}" + } + if { [info exists ::help] } { + puts stderr "${::help}\n" + } +} + +# +# intialize global vars +# Modulefile is something like +# +# ${PMODULES_ROOT}/group/${PMODULES_MODULEFILES_DIR}/name/version +# or +# ${PMODULES_ROOT}/group/${PMODULES_MODULEFILES_DIR}/X1/Y1/name/version +# or +# ${PMODULES_ROOT}/group/${PMODULES_MODULEFILES_DIR}/X1/Y1//X2/Y2/name/version +# +proc _pmodules_init_global_vars { } { + global group + global name + global version + global implementation + global PREFIX # prefix of package + + debug "$::ModulesCurrentModulefile" + set ::PmodulesRoot $::env(PMODULES_ROOT) + set ::PmodulesModulfilesDir $::env(PMODULES_MODULEFILES_DIR) + set modulefile [file split $::ModulesCurrentModulefile] + set pmodules_root [file split $::PmodulesRoot] + set pmodules_root_num_dirs [llength $pmodules_root] + + set modulefile_root [file join {*}[lrange $modulefile 0 [expr $pmodules_root_num_dirs - 1]]] + if { $::PmodulesRoot != $modulefile_root } { + debug "stop sourcing: ${::PmodulesRoot} != $modulefile_root" + return + } + debug "modulefile is inside our root" + set rel_modulefile [lrange $modulefile [llength $pmodules_root] end] + set group [lindex $rel_modulefile 0] + set name [lindex $modulefile end-1] + set version [lindex $modulefile end] + set implementation [lrange $rel_modulefile 2 end] + set prefix "$pmodules_root $group [lreverse_n $implementation 2]" + set PREFIX [file join {*}$prefix] + + debug "PREFIX=$PREFIX" + debug "group of module $name: $group" +} + +proc _pmodules_output_message { fname } { + if { [ file exists "${fname}" ] } { + set fp [open "${fname}" r] + set info_text [read $fp] + close $fp + puts stderr ${info_text} + } +} + +if { [info exists ::whatis] } { + module-whatis "$whatis" +} + +_pmodules_init_global_vars + +# +# we cannot load another module with the same name +# +conflict $name + +if { [module-info mode load] } { + debug "${name}/${version}: loading ... " + _pmodules_output_message "${PREFIX}/.info" + _pmodules_load_dependencies "${PREFIX}/.dependencies" +} + +_pmodules_setenv ${PREFIX} ${name} ${version} +_pmodules_update_loaded_modules ${group} ${name} ${version} + +debug "return from lib" diff --git a/Pmodules/libpmodules.bash b/Pmodules/libpmodules.bash new file mode 100644 index 0000000..9e7d27e --- /dev/null +++ b/Pmodules/libpmodules.bash @@ -0,0 +1,157 @@ +#!/bin/bash + +if [[ -z ${bindir} ]]; then + local bindir=$(dirname "${BASH_SOURCE}") + bindir=$(cd "${bindir}"/.. && pwd)"/bin" +fi + +pmodules::get_options() { + "${bindir}/getopt" "$@" +} + +pmodules::check_env_vars() { + [[ -n "${PMODULES_ROOT}" ]] && + [[ -n "${PMODULES_CONFIG_DIR}" ]] && + [[ -n "${PMODULES_TEMPLATES_DIR}" ]] && + [[ -n "${PMODULES_HOME}" ]] && + [[ -n "${PMODULES_VERSION}" ]] || std::die 1 " +Error: the module environment you are going to use as source has not been +initialized properly!" +} + +pmodules::check_directories() { + local -r src_prefix="$1" + + [[ -d "${src_prefix}" ]] && + [[ -d "${src_prefix}/${PMODULES_CONFIG_DIR}" ]] && + [[ -d "${src_prefix}/${PMODULES_TEMPLATES_DIR}" ]] && + [[ -d "${src_prefix}/Tools/Pmodules/${PMODULES_VERSION}" ]] || std::die 1 " +Error: the module environment '${src_prefix}' has not been initialized properly!" +} + +pmodules::check_env() { + pmodules::check_env_vars + pmodules::check_directories "${PMODULES_ROOT}" +} + + +std::log() { + local -ri fd=$1 + local -r fmt="$2\n" + shift 2 + printf -- "$fmt" "$@" 1>&$fd +} + +std::info() { + std::log 2 "$1" "${@:2}" +} + +std::error() { + std::log 2 "$1" "${@:2}" +} + +std::debug() { + [[ ${PMODULES_DEBUG} ]] || return 0 + std::log 2 "$@" +} + +std::die() { + local -ri ec=$1 + shift + if [[ -n $@ ]]; then + local -r fmt=$1 + shift + std::log 2 "$fmt" "$@" + fi + exit $ec +} + +# +# get answer to yes/no question +# +# $1: prompt +# +std::get_YN_answer() { + local -r prompt="$1" + local ans + read -p "${prompt}" ans + case ${ans} in + y|Y ) + return 0;; + * ) + return 1;; + esac +} + +# +# return normalized abolute pathname +# $1: filename +std::get_abspath() { + local -r fname=$1 + [[ -r "${fname}" ]] || return 1 + if [[ -d ${fname} ]]; then + echo $(cd "${fname}" && pwd) + else + local -r dname=$(dirname "${fname}") + echo $(cd "${dname}" && pwd)/$(basename "${fname}") + fi +} + +std::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 +} + +std::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 +} + +std::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}" +} + +# +# split file name +# +std::split_fname() { + local -r savedIFS="${IFS}" + IFS='/' + local std__split_fname_result__=( $(echo "${@: -1}") ) + IFS=${savedIFS} + eval $1=\(\"\${std__split_fname_result__[@]}\"\) + if (( $# >= 3 )); then + eval $2=${#std__split_fname_result__[@]} + fi +} + +# Local Variables: +# mode: sh +# sh-basic-offset: 8 +# tab-width: 8 +# End: diff --git a/Pmodules/modmanage b/Pmodules/modmanage new file mode 100644 index 0000000..3f0ed89 --- /dev/null +++ b/Pmodules/modmanage @@ -0,0 +1,6 @@ +#!/bin/sh --noprofile + +unset BASH_ENV + +declare -r bindir=$( cd $(dirname $0) && pwd -P ) +"${bindir}/bash" --noprofile --norc "${bindir}/../libexec/modmanage.bash" "$@" diff --git a/Pmodules/modmanage.bash.in b/Pmodules/modmanage.bash.in new file mode 100755 index 0000000..87ed047 --- /dev/null +++ b/Pmodules/modmanage.bash.in @@ -0,0 +1,775 @@ +#!@PMODULES_HOME@/bin/bash + +# we have to unset CDPATH, otherwise 'cd' prints the directoy! +unset CDPATH + +# used for some output only +declare -r CMD=$(basename "$0") + +declare -r mydir=$(cd $(dirname "$0") && pwd) +declare -r prefix=$(dirname "${mydir}") +declare -r bindir="${prefix}/bin" +declare -r libdir="${prefix}/lib" +declare -r libexecdir="${prefix}/libexec" + +source "${libdir}/libpmodules.bash" + +PATH="${bindir}:${PATH}" + +_exit () { + std::die 1 "Interrupted..." +} + +_err () { + std::info "Oops: got an error in function '${FUNCNAME[1]}', line ${BASH_LINENO[0]}" + std::die 1 "Aborting ..." +} + +trap '_exit' INT TERM +trap '_err' ERR + + +# make sure that everything is used from this version +declare PMODULES_VERSION='@PMODULES_VERSION@' + +print_version() { + echo " +Pmodules @PMODULES_VERSION@ using Tcl Environment Modules @MODULES_VERSION@ +Copyright GNU GPL v2 +" 1>&2 +} + +usage() { + local -r prog=$(basename $0) + print_version + echo " +Usage: ${prog} [ switches ] [ subcommand ] [subcommand-args ] + +Switches: + --dry-run do nothing + --force force overwrite + +Available SubCommands and Args: + init [--src=] [--user=] + Initialize a new minimal Pmodule environment. + + install [--with=...] + Install matching modules + + sync [--delete] [--dst=] + Synchronize modules. +" +} + +declare force='no' +declare dry_run='no' +declare DRY='' +declare subcommand='' +declare sargs=() + +subcommand_help_init() { + echo " +init [--src=] [--user=] [--version=] + Initialize a new minimal Pmodule environment in directory + . The parameter must only be present if + ${prog} is executed as root. +" 1>&2 +} + +subcommand_help_install() { + echo " +install ... [--with=...] [--release=...] [--src=] + Install matching modules +" 1>&2 +} + +subcommand_help_sync() { + echo " +sync [--delete] [--dst=] + Synchronize environment modules and configuration files + from Pmodule environment to Pmodule environment + (default: currently active Pmodule environment). + Not yet implemented: + If --delete is given, unmarked modules present in + will be deleted. +" 1>&2 +} + +subcommand_help() { + if [[ $# == 0 ]]; then + usage + elif typeset -F subcommand_help_$1 > /dev/null 2>&1 ; then + # help for sub-command + subcommand_help_$1 + else + usage + fi +} + +# +# Derive the relative module installation path +# from the relative modulefile path +# +# $1: relative module file path +# +get_module_prefix() { + local -a comp=( ${1//\// } ) # split rel.path into components + local path="${comp[0]}" # result path + local -i i + for ((i=1; i<${#comp[@]}-1; i+=2)); do + path+="/${comp[$((-i-1))]}/${comp[$((-i))]}" + done + echo "${path}" +} + +# +# Derive the relative module release file path +# from the relative module file path +# +# $1: relative module file path +# +get_releasefile_name() { + echo "$(dirname "$1")/.release-$(basename "$1")" +} + +# +# Sync a module from one Pmodules environment to another: +# - sync module installation +# - sync modulefile +# - sync release file +# +# Note: +# We do not take care of files in $PMODULES_ROOT/$PMODULES_TEMPLATES_DIR. If +# the modulefile is a sym-link it is expected that the target exists. +# +# $1: relative modulefile path (something like: Tools/gnuplot/5.0.0) +# $2: source prefix of Pmodule environment +# $3: target prefix of Pmodule environment +# +sync_module() { + local -r rel_modulefile=$1 + local -r src_prefix=$2 + local -r target_prefix=$3 + + local -r rel_module_prefix=$( get_module_prefix "${rel_modulefile}" ) + local -r rel_releasefile=$( get_releasefile_name "${rel_modulefile}" ) + + # install/update module + if [[ ! -d "${target_prefix}/${rel_module_prefix}" ]] || [[ "${force}" == 'yes' ]]; then + $DRY mkdir -p "${target_prefix}/${rel_module_prefix}" || return $? + $DRY rsync --links --perms --recursive --delete \ + "${src_prefix}/${rel_module_prefix}/" \ + "${target_prefix}/${rel_module_prefix}/" 2>/dev/null || return $? + fi + local -r src_modulefile="${src_prefix}/${rel_modulefile}" + local -r src_releasefile="${src_prefix}/${rel_releasefile}" + local -r target_modulefile="${target_prefix}/${rel_modulefile}" + local -r target_releasefile="${target_prefix}/${rel_releasefile}" + + # create target directory for module- and release-file + if [[ -e "${src_modulefile}" ]] || [[ -e "${src_releasefile}" ]]; then + local dir=$( dirname "${target_modulefile}" ) + $DRY mkdir -p "${dir}" || return $? + fi + + # copy modulefile + if [[ -e "${src_modulefile}" ]]; then + $DRY rsync --links --perms --recursive \ + "${src_modulefile}" "${target_modulefile}" 2>/dev/null || return $? + fi + + # copy release-file + if [[ -e "${src_releasefile}" ]]; then + $DRY rsync --links --perms --recursive \ + "${src_releasefile}" "${target_releasefile}" 2>/dev/null|| return $? + fi +} + +# +# Sync the Pmodules configuration and templates +# +# $1: source prefix of Pmodule environment +# $2: target prefix of Pmodule environment +# +sync_config() { + src="$1/${PMODULES_CONFIG_DIR}/" + dst="$2/${PMODULES_CONFIG_DIR}/" + $DRY rsync --recursive --links --perms --delete \ + "${src}" "${dst}" 2>/dev/null || return $? + sed -i.bak "s/PMODULES_VERSION=\(.*\)/PMODULES_VERSION=${PMODULES_VERSION}/" "${dst}/environment.bash" + echo + + src="$1/${PMODULES_TEMPLATES_DIR}/" + dst="$2/${PMODULES_TEMPLATES_DIR}/" + $DRY rsync --recursive --links --perms --delete --exclude="${src}/.git*" \ + "${src}" "${dst}" 2>/dev/null || return $? + echo +} + +# +# Delete a module +# +# $1: relative modulefile path +# $2: target prefix of Pmodule environment +# +delete_module() { + echo "Not implemented yet!" +} + +subcommand_init() { + local src='' + local target_prefixes=() + local user='' + local opts='' + opts=$(pmodules::get_options -o h -l src: -l user: -l help -l version: -- "$@") + if [[ $? != 0 ]]; then + subcommand_help_init + exit 1 + fi + eval set -- "${opts}" + while (($# > 0)); do + case $1 in + --src ) + src=$2 + shift + ;; + --user ) + user=$2 + shift + ;; + --version ) + PMODULES_VERSION=$2 + shift + ;; + -- ) + : + ;; + -* | -h | --help ) + echo "$1: illegal option" 1>&2 + subcommand_help_init + exit 1 + ;; + * ) + target_prefixes+=( "$1" ) + ;; + esac + shift + done + (( ${#target_prefixes[@]} != 0 )) || \ + std::die 1 "Error: no target directory specified!" + + # if source directory is not passed as argument, derive it from script name + if [[ -z "${src}" ]]; then + src=$(cd "${bindir}/../../../.." && pwd) + fi + [[ -d "${src}" ]] || \ + std::die 1 "Error: ${src}: source directory does not exist!" + [[ -r "${src}/config/profile.bash" ]] || \ + std::die 1 "Error: ${src}: shell profile does not exist or is not readable!" + source "${src}/config/profile.bash" + + local -i euid=$(id -u) + if (( euid == 0 )); then + [[ -n "${user}" ]] || \ + std::die 1 "Error: --user parameter is required!" + id -u "${user}" > /dev/null 2>&1 || \ + std::die 1 "Error: Unable to retrieve user id of user '${user}'" + else + [[ -z "${user}" ]] || \ + std::die 1 "Error: --user option is only allowed if running as root!" + fi + + pmodules::check_env || \ + std::die 1 "Giving up..." + + echo " +Attempting to create a minimal module environment from the +environment at '${PMODULES_ROOT}' +" + + init_pmodules_environment() { + local -r src_prefix="${PMODULES_ROOT}" + local -r target_prefix=$1 + local src='' + local dst='' + echo "Initializing target directory '${target_prefix}' ..." + echo + if [[ -d "${target_prefix}" ]] && [[ ${force} == no ]]; then + echo "Warning: ${target_prefix} already exists." + std::get_YN_answer "Do you really want to re-run the initialization? (y/N) " || \ + std::die 1 "Abort ..." + fi + force='yes' + echo "Creating target directory '${target_prefix}'..." + $DRY mkdir -p "${target_prefix}" || \ + std::die 1 "Error: make directory failed!" + echo + + echo "Syncing configuration ..." + sync_config "${PMODULES_ROOT}" \ + "${target_prefix}" || \ + std::die 1 "Error: configuration synchronization failed!" + + echo "Syncing Pmodules ${PMODULES_VERSION} from '${src_prefix}' to '${target_prefix}'..." + sync_module "Tools/Pmodules/${PMODULES_VERSION}" \ + "${src_prefix}" \ + "${target_prefix}" || \ + std::die 1 "Error: sync Pmodules failed!" + mkdir -p "${target_prefix}/Tools/${PMODULES_MODULEFILES_DIR}" + echo + + dst="${target_prefix}/${PMODULES_CONFIG_DIR}/environment.bash" + echo "Adding installation source '${src_prefix}' to '${dst}'..." + sed -i.bak '/PMODULES_INSTALL_SOURCE/d' "${dst}" + echo "declare -x PMODULES_INSTALL_SOURCE=\"${src_prefix}\"" >> "${dst}" + echo + + if [[ -n "${user}" ]]; then + echo "Changing user of new module environment to '${user}'..." + $DRY chown -R "${user}" "${target_prefix}" || \ + std::die 1 "Error: changing owner failed!" + echo + fi + echo "New minimal module environment created at '${target_prefix}'." + echo "To use this environment, execute" + echo " sudo ln -fs ${target_prefix} /opt/psi" + echo " source /opt/psi/${PMODULES_CONFIG_DIR}/profile.bash" + } + + umask 022 + for target_prefix in "${target_prefixes[@]}"; do + init_pmodules_environment "${target_prefix}" + done + +} + +declare -a Groups=() +declare -A HierarchyDepths + +get_groups () { + local -r root="$1" + { + cd "${root}" + # for some unknown reason [A-Z]* doesn't work on (some?) SL6 systems + for f in [ABCDEFGHIJKLMNOPQRSTUVWXYZ]*; do + [[ -d ${f}/${PMODULES_MODULEFILES_DIR} ]] || continue + Groups+=( $f ) + done + }; +} + +# +# $1: root of modulefile hierarchy +get_hierarchy_depth () { + local -r root="$1" + local -a modulefiles_dir + std::split_fname modulefiles_dir "${PMODULES_MODULEFILES_DIR}" + local -ir off=$(( ${#modulefiles_dir[@]} + 3 )) + { + cd "${root}" + local group + for group in "${Groups[@]}"; do + local fname=$(find "${group}/${PMODULES_MODULEFILES_DIR}" \ + -depth \( -type f -o -type l \) -print -quit) + [[ -n ${fname} ]] || continue + #local -a tmp2=( ${fname//\// } ) + local -a tmp + std::split_fname tmp "${fname}" + (( HierarchyDepths[$group]=${#tmp[@]}-off )) + done + }; +} + +subcommand_install() { + local opts='' + local -a with=() + local -a releases=() + local -a module_pattern=() + local src_prefix="${PMODULES_INSTALL_SOURCE}" + local -r target_prefix="${PMODULES_ROOT}" + local modulefile='' + local -A modules_to_install + local -A dependencies_to_install + local -A map_to_family + local -a initial_modulepath=() + + # + # Resolve dependencies to given module + # + # $1: modulefile relativ to src prefix. Something like: + # MPI/modulefiles/gcc/4.9.2/openmpi/1.8.4/hdf5/1.8.14 + # + # Notes: + # The variables + # initial_modulepath + # modules_to_install + # map_to_family + # from the calling function are used! + # + resolve_dependencies_of_module () { + local -r modulefile=$1 + local -a modulepath=( "${initial_modulepath[@]}" ) + + # compute filename with dependencies of given module + local -i i=0 n=0 + std::split_fname items n "${modulefile}" + local fname_dependencies="${src_prefix}/${items[0]}" + for (( i = n-2; i >= 2; i-=2 )); do + fname_dependencies+="/${items[$i]}/${items[i+1]}" + done + fname_dependencies+='/.dependencies' + [[ -r ${fname_dependencies} ]] || return 0 + + # loop over all dependecies + local dep + while read dep; do + # skip empty lines + # :FIXME: skip comments?! + [[ -z ${dep} ]] && continue + + # search for module with current modulepath and remember + local modulename=$(find "${modulepath[@]}" -path "*/${dep}" 2>/dev/null | head -n 1 ) + [[ -n ${modulename} ]] || \ + std::die 3 "Oops: required module '${dep}' not found!" + modulename=${modulename/${src_prefix}\/} + dependencies_to_install[${modulename}]='.' + resolve_dependencies_of_module "${modulename}" + # append new node in hierarchy to modulepath + if [[ -n ${map_to_family[${dep}]} ]]; then + local path="${src_prefix}/${map_to_family[${dep}]}/${PMODULES_MODULEFILES_DIR}/" + path+="${modulename/*\/${PMODULES_MODULEFILES_DIR}\/}" + modulepath+=( "${path}" ) + fi + done < "${fname_dependencies}" + } + + print_modules_to_install() { + local modulefile + std::info "The following modules will be installed/updated:" + for modulefile in "${!modules_to_install[@]}"; do + if [[ -e "${target_prefix}/${modulefile}" ]]; then + std::info " Updating: ${modulefile/\/${PMODULES_MODULEFILES_DIR}\//: }" + else + std::info " Installing: ${modulefile/\/${PMODULES_MODULEFILES_DIR}\//: }" + fi + done 2>&1 | sort + if (( ${#dependencies_to_install[@]} > 0 )); then + std::info "\nThe following dependencies will be installed/updated:" + for modulefile in "${!dependencies_to_install[@]}"; do + if [[ -e "${target_prefix}/${modulefile}" ]]; then + std::info " Updating: ${modulefile/\/${PMODULES_MODULEFILES_DIR}\//: }" + else + std::info " Installing: ${modulefile/\/${PMODULES_MODULEFILES_DIR}\//: }" + fi + done 2>&1 | sort + fi + std::info "" + std::get_YN_answer "Do you want to continue? [n] " || \ + std::die 1 "Aborting..." + std::info "" + } + + opts=$(pmodules::get_options -o hf -l dry-run -l force -l with: -l release: -l help -l src: -- "$@") + if [[ $? != 0 ]]; then + subcommand_help_install + exit 1 + fi + eval set -- "${opts}" + while (($# > 0)); do + case $1 in + --dry-run ) + DRY='echo' + ;; + --force | -f ) + force='yes' + ;; + --release ) + releases+=( "$2" ) + shift + ;; + --src ) + src_prefix="$2" + shift + ;; + --with ) + with+=( "$2" ) + shift + ;; + -- ) + : + ;; + -h | --help ) + subcommand_help_install + exit 1 + ;; + -* ) + echo "$1: illegal option" 1>&2 + subcommand_help_init + exit 1 + ;; + * ) + module_pattern+=( "$1" ) + ;; + esac + shift + done + + [[ -n ${src_prefix} ]] \ + || std::die 3 "Oops: no installation source given." + [[ -d ${src_prefix} ]] \ + || std::die 3 "Oops: '${src_prefix}' is not a valid installation source." + + # scan available groups and their depth + get_groups "${src_prefix}" + get_hierarchy_depth "${src_prefix}" + + # set initial modulepath + local group + for group in "${!HierarchyDepths[@]}"; do + if (( ${HierarchyDepths[${group}]} == 0 )); then + initial_modulepath+=( "${src_prefix}/${group}/${PMODULES_MODULEFILES_DIR}" ) + fi + done + + # + # create a mapping from module name to their family. + # Examples: + # gcc/5.2.0 -> Compiler + # openmpi/1.8.4 -> MPI + local _fname='' + while read _fname; do + local _family="${_fname%/${PMODULES_MODULEFILES_DIR}/*}" + local -a items + std::split_fname items "${_fname#*/${PMODULES_MODULEFILES_DIR}/}" + local -i n=${#items[*]} + # We are only interested in families adding something to + # the modulepath. + if (( n >= 4 )); then + local _key=$( IFS='/'; echo "${items[*]:$n-4:2}" ) + map_to_family[$_key]=${_family} + fi + done < <({ cd "${src_prefix}" && \ + find */"${PMODULES_MODULEFILES_DIR}" \ + \( -type l -o -type f \) \! -name ".*"; } 2>/dev/null ) + + # + # search for to be installed modules and their dependencies + # + local -i n=0 + while read modulefile; do + resolve_dependencies_of_module "${modulefile}" + modules_to_install["${modulefile}"]+='.' + let n+=1 + done < <(${PMODULES_HOME}/bin/modulecmd bash search \ + "${module_pattern[@]}" \ + "${with[@]/#/--with=}" \ + "${releases[@]/#/--release=}" \ + --no-header --print-modulefiles \ + --src="${src_prefix}" 2>&1 1>/dev/null) + (( n == 0 )) && \ + std::die 0 "No matching modules found ..." + print_modules_to_install + + # install ... + for modulefile in "${!modules_to_install[@]}" "${!dependencies_to_install[@]}"; do + if [[ -e "${target_prefix}/${modulefile}" ]]; then + std::info " Updating: ${modulefile/\/${PMODULES_MODULEFILES_DIR}\//: }" + else + std::info " Installing: ${modulefile/\/${PMODULES_MODULEFILES_DIR}\//: }" + fi + sync_module "${modulefile}" \ + "${src_prefix}" \ + "${target_prefix}" + done + std::info "\nDone!\n" +} + +# +# delete specified module(s) +# +subcommand_delete() { + : +} + +# +# remove modules which have been removed in our source +# +subcommand_cleanup() { + : +} + +subcommand_sync() { + local delete=false + local opts='' + local dst_prefix='' + local src_prefix='' + + opts=$(pmodules::get_options -o h -l dst: -l delete -l help -- "$@") + if [[ $? != 0 ]]; then + subcommand_help_sync + exit 1 + fi + eval set -- "${opts}" + while (($# > 0)); do + case $1 in + --dst ) + dst_prefix="$2" + shift + ;; + --delete ) + delete=true + ;; + -- ) + : + ;; + -* | -h | --help ) + echo "$1: illegal option" 1>&2 + subcommand_help_init + exit 1 + ;; + * ) + [[ -n "${src_prefix}" ]] && \ + std::die 1 "Error: Only one source is allowed!" + src_prefix="$1" + ;; + esac + shift + done + unset -v opts + + if [[ -z "${dst_prefix}" ]]; then + dst_prefix="${PMODULES_ROOT}" + fi + ( + PMODULES_ROOT="${dst_prefix}" pmodules::check_env || \ + std::die 1 "Error: invalid destination modules environment!" + ) || std::die 1 "Giving up..." + + : ${src_prefix:=${PMODULES_INSTALL_SOURCE}} + if [[ -z "${src_prefix}" ]]; then + std::die 1 "Error: no source module environment was specified!" + fi + ( + PMODULES_ROOT="${src_prefix}" pmodules::check_env || \ + std::die 1 "Error: invalid source modules environment!" + ) || std::die 1 "Giving up..." + [[ "$( cd "$src_prefix"; pwd -P )" == "$( cd "$dst_prefix"; pwd -P )" ]] && \ + std::die 1 "Error: source and destination are equal!" + local modbin=${PMODULES_HOME#"${PMODULES_ROOT}/"}/bin/modulecmd.tcl + local file_type_src=$( file -b "${src_prefix}/${modbin}" 2>&1 || echo err1 ) + local file_type_dst=$( file -b "${dst_prefix}/${modbin}" 2>&1 || echo err2 ) + [[ "${file_type_src}" == "${file_type_dst}" ]] || \ + std::die 1 "Error: The file signatures in the source and destination installation do not match!" + unset -v file_type_src file_type_dst + local dialog_script="${PMODULES_HOME}/bin/dialog.bash" + [[ -r "$dialog_script" ]] || \ + std::die 1 "Error: Unable to find dialog script of installation $dialog_script"; + + DIALOG_LIB=1 # use dialog script as a library + source "$dialog_script" # dialog functions + + # Redefine module_out to append modules to the selected_modules variable + local -a selected_modules + module_out() { + local -a args=(${modlist[$1]}) + local path="" + IFS=/ + [[ -n "${args[3]}" ]] && path="/${args[*]:3}" + unset IFS + selected_modules+=( "${args[2]}${path}/${args[0]}" ) + } + + module_picker "${dst_prefix}" "${src_prefix}" || { + # this calls module_out for each selected module, + #filling up the selected_modules array + echo "Abort!" + exit 1 + } + + local -a destination_modules=( $(cd "${dst_prefix}/${PMODULES_MODULEFILES_DIR}"; find -L . -type f | while read f; do n=${f##*/}; [[ "${n:0:1}" == "." ]] || echo ${f#./}; done) ) + + # redefine set difference, the version in dialog.bash only handles integers + set_difference() { # $1 \ $2 + local -a operand1=($1) + local -a operand2=($2) + local -A members + local elem + for elem in "${operand1[@]}"; do + members[$elem]=1 + done + for elem in "${operand2[@]}"; do + unset members[$elem] + done + echo ${!members[@]} + } + + if [[ "$delete" == "true" ]]; then + local -a modules_delete=( $(set_difference "${destination_modules[*]}" "${selected_modules[*]}") ) + for m in "${modules_delete[@]}"; do + echo "Deleting module $m ..." + delete_module "$m" "$dst_prefix" + done + unset modules_delete + fi + + local -a modules_copy=( $(set_difference "${selected_modules[*]}" "${destination_modules[*]}") ) + if [[ -n $modules_copy ]]; then + echo "Syncing configuration ..." + sync_config "$src_prefix" "$dst_prefix" || \ + std::die 1 "Error: syncing the configuration failed" + fi + for m in "${modules_copy[@]}"; do + echo "Copying module $m ..." + sync_module "$m" "$src_prefix" "$dst_prefix" || \ + std::die 1 "Error: syncing of module $m failed!" + done + unset modules_copy +} + +while (($# > 0)); do + case $1 in + -h | -H | -\? | --help | -help ) + usage + exit 1 + ;; + -V | --version ) + print_version + exit 1 + ;; + -f | --force ) + force='yes' + ;; + --dry-run ) + dry_run='yes' + DRY='echo' + ;; + -* ) + echo "$1: unknown switch.\n" 1>&2 + exit 1 + ;; + init|install|sync|help ) + subcommand="subcommand_$1" + shift + sargs=( $* ) + shift $# + ;; + * ) + echo "$1: unknown sub-command" 1>&2 + exit 1 + esac + shift || : +done + +if [[ -z ${subcommand} ]]; then + usage + exit 1 +fi +[[ -z "${PMODULES_ROOT}" ]] && \ + std::die 1 "Error: No current module environment is configured!" +$subcommand "${sargs[@]}" + +# Local Variables: +# mode: sh +# sh-basic-offset: 8 +# tab-width: 8 +# End: diff --git a/Pmodules/modsync.bash b/Pmodules/modsync.bash new file mode 100755 index 0000000..3b15c17 --- /dev/null +++ b/Pmodules/modsync.bash @@ -0,0 +1,236 @@ +#!/usr/bin/env bash + +declare -r DEFAULT_SRC="/afs/psi.ch/sys/psi.@sys" +declare -r DEFAULT_DST="/opt/psi.local" + +usage() { + echo " +$0 [--from=] [--to=] [--dryrun] [--delete] + --from source module installation (default: $DEFAULT_SRC) + + --to destination module installation (default: $DEFAULT_DST) + + --dryrun just tell what would be copied and deleted + + --delete as an additional task, delete modules that are present + at the destination but not at the source installation + (destination cleanup)" >&2 +} + +die() { + echo "$1" >&2 + exit 1 +} + +# check if directory $1 is a valid prefix +is_module_prefix() { + if [[ -d "$1" ]] && + [[ -d "$1/$PMODULES_CONFIG_DIR" ]] && + [[ -d "$1/$PMODULES_MODULEFILES_DIR" ]] + then + return 0 + fi + return 1 +} + +# set the source and destination module installations +get_options() { + local src_dir="$DEFAULT_SRC" + local dst_dir="$DEFAULT_DST" + local dryrun=false + local delete=false + local option + + while (($# > 0)); do + if [[ "${1#--from}" != "$1" ]]; then + option="${1#--from}" + option="${option#=}" + [[ -z "$option" ]] && { shift; option="$1"; } + src_dir="$option" + elif [[ "${1#--to}" != "$1" ]]; then + option="${1#--to}" + option="${option#=}" + [[ -z "$option" ]] && { shift; option="$1"; } + dst_dir="$option" + elif [[ -z "${1#--dryrun}" ]]; then + dryrun=true + elif [[ -z "${1#--delete}" ]]; then + delete=true + else + usage > /dev/fd/2 + die "Unknown option: $1" + fi + shift + done + + is_module_prefix "$src_dir" || { die "<$src_dir> is not a Pmodules installation"; } + is_module_prefix "$dst_dir" || { die "<$dst_dir> is not a Pmodules installation"; } + src_dir=$( cd "$src_dir"; pwd -P ) + dst_dir=$( cd "$dst_dir"; pwd -P ) + [[ "$src_dir" == "$dst_dir" ]] && { die "same source and destination installations"; } + local modbin=$( cd "$PMODULES_HOME"; pwd -P ) + local prefix=$( cd "$PMODULES_PREFIX"; pwd -P ) + modbin=${modbin#"$prefix/"}/bin/modulecmd + local -r file_type_src=$( file -b "$src_dir/$modbin" 2>&1 || echo err1 ) + local -r file_type_dst=$( file -b "$dst_dir/$modbin" 2>&1 || echo err2 ) + [[ ! "${file_type_src}" == "${file_type_dst}" ]] || { + die "The file signatures in the source and destination installation do not match!" + } + echo "$src_dir" "$dst_dir" "$dryrun" "$delete" +} + +# Derive the relative module installation path +# from the relative module file path +# $1 relative module file path +get_modpath() { + local -a comp=( ${1//\// } ) # split rel.path into components + local -a path # result path + local -i i + for ((i=0; i<${#comp[@]}; i++)); do + case $i in + 0) path=( ${comp[0]%.*} );; + *) path+=( "${comp[$((-i-1))]}/${comp[$((-i))]}" ); i+=1;; + esac + done + echo "${path[*]}" +} + +# Derive the relative module release file path +# from the relative module file path +# $1 relative module file path +get_release_path() { + echo "$(dirname "$1")/.release-$(basename "$1")" +} + +# $1 dryrun=(true|false) +# $2 relative module file path of destination module to be deleted +# $3 destination prefix +delete_module() { + if [[ "$1" != "false" ]]; then + echo "(dryrun) delete: $2 at $3" 1>&2 + return 0 + fi + local modpath=$( get_modpath "$2" ) + [[ -z "$modpath" ]] && { + die "Unable to retrieve module file and installation paths"; + } + echo "rm -v \"$3/$PMODULES_MODULEFILES_DIR/$2\"" + echo "rm -v \"$3/$PMODULES_MODULEFILES_DIR/$( get_release_path $2 )\"" + echo "rmdir -vp \"$( dirname "$3/$PMODULES_MODULEFILES_DIR/$2" )\"" + echo "rm -vrf \"$3/$modpath\"" + echo "rmdir -vp \"$( dirname "$3/$modpath" )\"" + echo "deleted: $2" 1>&2 +} + +# $1 dryrun=(true|false) +# $2 relative module file path of source module to be copied to the destination +# $3 source prefix +# $4 destination prefix +copy_module() { + if [[ "$1" != "false" ]]; then + echo "(dryrun) copy: $2 from $3 to $4" 1>&2 + return 0 + fi + local modpath=$( get_modpath "$2" ) + [[ -z "$modpath" ]] && { die "Unable to retrieve module file and installation paths"; } + install -d $( dirname "$3/$PMODULES_MODULEFILES_DIR/$2" ) + ( + cd $3 + rsync --links --perms --relative --verbose "$PMODULES_MODULEFILES_DIR/$2" "$4" + rsync --links --perms --relative --verbose "$PMODULES_MODULEFILES_DIR/$( get_release_path "$2" )" "$4" + rsync --recursive --links --perms --relative --verbose "$modpath" "$4" + ) + echo "copied: $2" 1>&2 +} + +# syncronize modules from source to +# destination module installations +# --from= default: /afs/psi.ch/sys/psi.@sys +# --to= default: /opt/psi.local +sync_modules() { + local -a options=( $(get_options "$@") ) + [[ -z "$options" ]] && exit 1 + local src_dir="${options[0]}" + local dst_dir="${options[1]}" + local dryrun="${options[2]}" + local delete="${options[3]}" + unset options + + local profile_script="$src_dir/$PMODULES_CONFIG_DIR/profile.bash" + [[ -r "$profile_script" ]] || { + die "Unable to find profile script of installation $profile_script"; + } + local search_script="$src_dir/Tools/Pmodules/${PMODULES_VERSION}/bin/modulecmd" + [[ -x "$search_script" ]] || { + die "Unable to find search script of installation $search_script"; + } + local dialog_script="$src_dir/Tools/Pmodules/${PMODULES_VERSION}/bin/dialog.bash" + [[ -r "$dialog_script" ]] || { + die "Unable to find dialog script of installation $dialog_script"; + } + + . "$profile_script" # set variables for the source installation + + DIALOG_LIB=1 # use dialog script as a library + . "$dialog_script" # dialog functions + + local -a selected_modules + + # Redefine module_out to append modules to the selected_modules variable + module_out() { + local -a args=(${modlist[$1]}) + local path="" + IFS=/ + [[ -n "${args[3]}" ]] && path="/${args[*]:3}" + unset IFS + selected_modules+=( "${args[2]}$path/${args[0]}" ) + } + + module_picker "$dst_dir" < <("$search_script" bash search --no-header -a 2>&1) + + local -a destination_modules=( $(cd "$dst_dir/$PMODULES_MODULEFILES_DIR"; find -L . -type f | while read f; do echo ${f#./}; done) ) + + # redefine set difference, the version in dialog.bash only handles integers + set_difference() { # $1 \ $2 + local -a operand1=($1) + local -a operand2=($2) + local -A members + local elem + for elem in "${operand1[@]}"; do + members[$elem]=1 + done + for elem in "${operand2[@]}"; do + unset members[$elem] + done + echo ${!members[@]} + } + + [[ "$delete" == "true" ]] && { + local -a modules_delete=( $(set_difference "${destination_modules[*]}" "${selected_modules[*]}") ) + for m in "${modules_delete[@]}"; do + delete_module "$dryrun" "$m" "$dst_dir" + done + unset modules_delete + } + + + local -a modules_copy=( $(set_difference "${selected_modules[*]}" "${destination_modules[*]}") ) + [[ -z $modules_copy ]] || { + if [[ "$dryrun" != "false" ]]; then + echo "(dryrun) update: $dst_dir/$PMODULES_CONFIG_DIR from $src_dir/$PMODULES_CONFIG_DIR" 1>&2 + else + ( + local -a extraoption="$( [[ "$delete" == "true" ]] && echo --delete )" + cd "$src_dir" + rsync --recursive --links --perms --relative $extraoption --verbose --exclude .git "$PMODULES_CONFIG_DIR" "$dst_dir" + echo "updated: $PMODULES_CONFIG_DIR from $src_dir" 1>&2 + ) + fi + for m in "${modules_copy[@]}"; do + copy_module "$dryrun" "$m" "$src_dir" "$dst_dir" + done + } + unset modules_copy +} + +sync_modules "$@" diff --git a/Pmodules/modulecmd b/Pmodules/modulecmd new file mode 100644 index 0000000..89ca1ab --- /dev/null +++ b/Pmodules/modulecmd @@ -0,0 +1,6 @@ +#!/bin/sh --noprofile + +unset BASH_ENV + +declare -r bindir=$( cd $(dirname $0) && pwd -P ) +"${bindir}/bash" --noprofile --norc "${bindir}/../libexec/modulecmd.bash" "$@" diff --git a/Pmodules/modulecmd.bash.in b/Pmodules/modulecmd.bash.in new file mode 100755 index 0000000..918b751 --- /dev/null +++ b/Pmodules/modulecmd.bash.in @@ -0,0 +1,1666 @@ +#!@PMODULES_HOME@/bin/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 -r prefix=$(dirname "${mydir}") +declare -r bindir="${prefix}/bin" +declare -r libdir="${prefix}/lib" +declare -r libexecdir="${prefix}/libexec" + +source "${libdir}/libpmodules.bash" + +PATH="${bindir}:${PATH}" + +declare -r version='@PMODULES_VERSION@' +declare -r modulecmd="${libexecdir}/modulecmd.tcl" + +declare -rx TCL_LIBRARY="${libdir}/tcl8.6" + +# required for pre 0.99.3 modulefiles +declare -rx PSI_LIBMODULES="${TCL_LIBRARY}/libmodules.tcl" + +declare verbosity_lvl=${PMODULES_VERBOSITY:-'verbose'} + +shopt -s nullglob +declare -a Groups='()' +declare -A HierarchyDepths='()' + +export_env() { + local s='' + if [[ "${shell}" == "bash" ]]; then + while (( $# > 0 )); do + echo -n "export $1=\"${!1}\";" + shift + done + return + elif [[ "${shell}" == "tcsh" ]]; then + while (( $# > 0 )); do + echo "setenv $1 ${!1}" + shift + done + return + fi +} + +save_env() { + local s='' + local tmp + while (( $# > 0 )); do + tmp="$( typeset -p $1 2> /dev/null)" + [[ -n "${tmp}" ]] && s+="${tmp};" + shift + done + declare -g PMODULES_ENV=$( "${PMODULES_HOME}/bin/base64" --wrap=0 <<< "$s" ) + export_env PMODULES_ENV +} + +trap 'save_env Groups HierarchyDepths UsedReleases PMODULES_DEFAULT_GROUPS PMODULES_DEFINED_RELEASES PMODULES_DEFAULT_RELEASES' EXIT + +print_version() { + echo " +Pmodules ${version} using Tcl Environment Modules @MODULES_VERSION@ +Copyright GNU GPL v2 +" 1>&2 +} + +usage() { + print_version + echo " +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 +" 1>&2 + die 1 +} + +subcommand_help_add() { + echo " +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. +" 1>&2 + die 1 +} + +subcommand_help_load() { + subcommand_help_add +} + +subcommand_help_rm() { + echo " +USAGE: + module rm modulefile... + moudle unload modulefile... + Remove modulefile(s) from the shell environment. Removing + a 'group-head' will also unload all modules belonging to + this group. +" 1>&2 + die 1 +} + +subcommand_help_unload() { + subcommand_help_rm +} + +subcommand_help_switch() { + echo " +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() { + subcommand_help_switch +} + +subcommand_help_display() { + echo " +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() { + subcommand_help_display +} + +subcommand_help_apropos() { + echo " +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() { + subcommand_help_apropos +} + + +subcommand_help_avail() { + echo " +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. + + 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'. +" 1>&2 + die 1 +} + +subcommand_help_search() { + echo " +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. +" 1>&2 + die 1 +} + +subcommand_help_use() { + echo " +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. +" 1>&2 + die 1 +} + +subcommand_help_unuse() { + echo " +unuse directory|group|release... + Remove the given directory, group or release from the search + path. +" 1>&2 + die 1 +} +subcommand_help_update() { + echo " +USAGE: + module update + Attempt to reload all loaded modulefiles. +" 1>&2 + die 1 +} + +subcommand_help_refresh() { + echo " +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 " +USAGE: + module purge + Unload all loaded modulefiles. +" 1>&2 + die 1 +} + +subcommand_help_list() { + echo " +USAGE: + module list + List loaded modules. +" 1>&2 + die 1 +} + +subcommand_help_clear() { + echo " +USAGE: + module clear + Force the Modules package to believe that no modules are + currently loaded. +" 1>&2 + die 1 +} + +subcommand_help_whatis() { + echo " +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 " +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. +" 1>&2 + die 1 +} + +subcommand_help_initprepend() { + echo " +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 " +USAGE: + module initrm modulefile... + Remove modulefile(s) from the shell's initialization files. +" 1>&2 + die 1 +} + +subcommand_help_initswitch() { + echo " +USAGE: + module initswitch modulefile1 modulefile2 + Switch modulefile1 with modulefile2 in the shell's initialization files. +" 1>&2 + die 1 +} + +subcommand_help_initlist() { + echo " +USAGE: + module initlist + List all of the modulefiles loaded from the shell's initialization file. +" 1>&2 + die 1 +} + +subcommand_help_initclear() { + echo " +USAGE: + module initclear + Clear all of the modulefiles from the shell's initialization files. +" 1>&2 + die 1 +} + +# +# get release of module +# Note: +# - the release of a modulefile outside ${PMODULES_ROOT} is 'stable' +# - the release of a modulefile inside ${PMODULES_ROOT} without a +# coresponding release file is 'unstable' +# +# Args: +# $1: absolute modulefile name +# +get_release() { + local -r modulefile=$1 + + # is modulefile outside ${PMODULES_ROOT}? + if [[ ! ${modulefile} =~ ${PMODULES_ROOT} ]]; then + echo 'stable' + return 0 + fi + + # we are inside ${PMODULES_ROOT} + local -r releasefile="${modulefile%/*}/.release-${modulefile##*/}" + if [[ -r ${releasefile} ]]; then + # read releasefile, remove empty lines, spaces etc + local -r data=$( < "${releasefile}" ) + echo ${data} + else + echo 'unstable' + fi + return 0 +} + +: ${PMODULES_DEFINED_RELEASES:=':unstable:stable:deprecated:'} + +is_release() { + [[ ${PMODULES_DEFINED_RELEASES} =~ :$1: ]] +} + +is_used_release() { + [[ ":${UsedReleases}:" =~ :$1: ]] +} + +declare used_groups=":${PMODULES_USED_GROUPS}:" + +is_group () { + [[ -n ${group} ]] && [[ -n ${HierarchyDepths[${group}]} ]] +} + +is_used_group() { + [[ ${used_groups} =~ :$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}" == "#%Module1.0" ]] +} + +subcommand_generic0() { + local -r subcommand=$1 + shift + local opts='' + opts=$(pmodules::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_generic1() { + local -r subcommand=$1 + shift + local opts='' + opts=$(pmodules::get_options -- '' "$@") || subcommand_help_${subcommand} + eval set -- "${opts}" + local args=() + while (( $# > 0 )); do + case $1 in + -- ) + ;; + * ) + if (( ${#args[@]} == 0 )); then + args+=( "$1" ) + else + die 3 "${CMD} ${subcommand}: only one argument allowed" + fi + ;; + esac + shift + done + if (( ${#args[@]} == 0 )); then + die 3 "${CMD} ${subcommand}: missing argument" + fi + "${modulecmd}" "${shell}" "${subcommand}" "${args[@]}" +} + +subcommand_generic1plus() { + local -r subcommand=$1 + shift + local opts='' + opts=$(pmodules::get_options -- '' "$@") || subcommand_help_${subcommand} + eval set -- "${opts}" + local args=() + while (( $# > 0 )); do + case $1 in + -- ) + ;; + * ) + args+=( "$1" ) + ;; + esac + shift + done + if (( ${#args[@]} == 0 )); then + die 3 "${CMD} ${subcommand}: missing argument" + fi + "${modulecmd}" "${shell}" "${subcommand}" "${args[@]}" +} + +subcommand_generic1or2() { + local -r subcommand=$1 + shift + local opts='' + opts=$(pmodules::get_options -- '' "$@") || subcommand_help_${subcommand} + eval set -- "${opts}" + local args=() + while (( $# > 0 )); do + case $1 in + -- ) + ;; + * ) + if (( ${#args[@]} > 2 )); then + die 3 "${CMD} ${subcommand}: only one or two arguments are allowed" + fi + args+=( "$1" ) + ;; + esac + shift + done + if (( ${#args[@]} == 0 )); then + die 3 "${CMD} ${subcommand}: missing argument" + fi + "${modulecmd}" "${shell}" "${subcommand}" "${args[@]}" +} + +# +# load [-fsvw] +# +# $1: module to load +# +subcommand_load() { + local release='undef' + local moduledir='' + local m='' + + local saved_IFS=${IFS}; + IFS=':' + local -a modulepath=(${MODULEPATH}) + IFS=${saved_IFS} + local -r saved_MODULEPATH=${MODULEPATH} + local -a saved_modulepath=${modulepath} + local -r saved_UsedReleases=${UsedReleases} + + # + # Test whether a given module can be loaded according to the + # accepted releases. + # + # 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 + # + is_loadable() { + release=$( get_release "$1" ) + [[ :${UsedReleases}: =~ ${release} ]] && return 0 + return 1 + } + + # + # Test whether a given module is available. + # 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: + # The variable 'release' in function 'subcommand_load()' will be set. + # The variable 'm' in function 'subcommand_load()' may be set. + # + is_available() { + local 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=$(std::get_abspath "${m}") + fi + is_modulefile "${m}" || return 2 + is_loadable "${m}" || return 3 + if [[ "${m}" =~ "${PMODULES_ROOT}" ]]; then + for dir in "${modulepath[@]}"; do + [[ "${m}" =~ "${dir}" ]] && return 0 + done + return 4 + fi + return 0 + fi + + # check whether $m is in our modulepath + for dir in "${modulepath[@]}"; do + if [[ -d ${dir}/$1 ]]; then + # module specified without version, like 'hdf5' + while read fname; do + is_modulefile "${fname}" || return 2 + if is_loadable "${fname}"; then + moduledir="${dir}" + return 0 + fi + done < <(find "${dir}/$1" -mindepth 1 -maxdepth 1 -type l -o -type f \! -name ".*") + else + # module specified with name/version, like 'hdf5/1.8.14' + [[ -f ${dir}/$1 ]] || continue + [[ -r ${dir}/$1 ]] || continue + is_modulefile "${dir}/$1" || return 2 + if is_loadable "${dir}/$1"; then + moduledir="${dir}" + return 0 + fi + fi + done + return 1 + } + + # + # output load 'hints' + # + # Note: + # The variable 'm' from the parent function will be used + # but not changed. + # + # Args: + # none + output_load_hints() { + local -ra rels=( ${PMODULES_DEFINED_RELEASES//:/ } ) + for rel in "${rels[@]}"; do + eval $( subcommand_use "${rel}" ) + if is_available "${m}"; then + std::info "${m}: is ${rel}! If you want to load this module, run" + std::info "\tmodule use ${rel}" + std::info "before running" + std::info "\tmodule load ${m}" + exit 42 + fi + done + local something_found='no' + local -a output=() + local -a release=() + local -a loadable=() + local -i i=0 + local -i n=0 + while read -a line; do + output[n]="module load ${line[@]:3} ${line[0]}" + release[n]=${line[1]} + if [[ ":${UsedReleases}:" =~ "${release[n]}" ]]; then + loadable[n]='yes' + else + loadable[n]='no' + fi + n+=1 + done < <(subcommand_search "${m}" -a --no-header 2>&1) + std::info "${CMD} load: module unavailable -- ${m}" + if (( n > 0 )); then + # :FIXME: output group + std::info "\nBut the following modules chain(s) are available in the hierarchy:" + for ((i=n-1; i >=0; i--)); do + if [[ "${loadable[i]}" == "no" ]]; then + std::info "${output[i]}\t# ${release[i]}" + else + std::info "${output[i]}" + fi + done + fi + } + + local opts + opts=$(pmodules::get_options -o fsvw -l force -l silent -l verbose -l warn -- "$@") || \ + subcommand_help_load + eval set -- "${opts}" + local args=() + opts='' + while (($# > 0)); do + case $1 in + -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 + die 2 "${CMD} load: No module specified." + fi + for m in "${args[@]}"; do + # restore original MODULEPATH; it might have been overwritten + MODULEPATH=${saved_MODULEPATH} + modulepath=${saved_modulepath} + UsedReleases=${saved_UsedReleases} + if [[ "$m" =~ ":" ]]; then + + # extendet module name is either + # - group:name or + # - group:name:release or + # - release:name or + # - release:group:name or + # - name:release + + local save_ifs=${IFS} + IFS=':' + local -a toks=($m) + IFS=${save_ifs} + local group='' + local release='' + if is_group "${toks[0]}"; then + group=${toks[0]} + m=${toks[1]} + release=${toks[2]} + elif is_release "${toks[0]}"; then + release=${toks[0]} + if is_group "${toks[1]}"; then + group=${toks[1]} + m=${toks[2]} + else + m=${toks[1]} + group=${toks[2]} + fi + else + m=${toks[0]} + if is_group "${toks[1]}"; then + group=${toks[1]} + release=${toks[2]} + else + release=${toks[1]} + group=${toks[2]} + fi + fi + if [[ -n ${group} ]]; then + is_group "${group}" || die 3 "${CMD} load: illegal group name." + local -i depth=${HierarchyDepths[${group}]} + (( depth != 0 )) && die 3 "${CMD} load: illegal group name." + MODULEPATH="${PMODULES_ROOT}/${group}/${PMODULES_MODULEFILES_DIR}" + modulepath=( ${MODULEPATH} ) + fi + if [[ -n ${release} ]]; then + is_release "${release}" || die 3 "${CMD} load: illegal release name." + std::append_path UsedReleases "${release}" + fi + fi + if is_available "${m}"; then + if [[ ${verbosity_lvl} != silent ]] && [[ ${release} != stable ]]; then + std::info "Warning: the ${release} module '${m}' has been loaded." + fi + "${modulecmd}" "${shell}" ${opts} load "${m}" + else + if [[ ${verbosity_lvl} == 'verbose' ]]; then + output_load_hints + else + die 3 "${CMD} load: module unavailable -- ${m}" + fi + fi + done + # restore original MODULEPATH; it might have been overwritten + MODULEPATH=${saved_MODULEPATH} + UsedReleases=${saved_UsedReleases} +} + +# +# unload +# +subcommand_unload() { + # :FIXME: add dependency tests: don't unload if module is required be + # another module + while (( $# > 0 )); do + subcommand_generic1 unload "$1" + shift + done +} + +# +# swap [] +# +subcommand_swap() { + subcommand_generic1or2 swap "$@" +} + +# +# show +# +subcommand_show() { + while (( $# > 0 )); do + subcommand_generic1 show "$1" + shift + done +} + +# +# get all available modules in given directory. +# return list like +# modulename1 release1 modulename2 release2 ... +# +get_available_modules() { + local -r dir=$1 + local -r module=$2 + local -r use_releases=${3:-${UsedReleases}} + local -a mods=() + while read mod; do + local release=$( get_release "${dir}/${mod}" ) + + if [[ :${use_releases}: =~ :${release}: ]]; then + mods+=( "${mod}" ${release} ) + fi + done < <(MODULEPATH="${dir}" "${modulecmd}" bash -t avail "${module}" 2>&1 | tail -n +2) + echo "${mods[@]}" +} + +# +# avail [-hlt] [...] +# +subcommand_avail() { + # use this variable in the output functions + local -a mods=() + local dir='' + + # get number of columns of terminal + cols=$(tput cols) + + output_header() { + local caption=${dir/${PMODULES_ROOT}\/} + local caption=${caption/\/${PMODULES_MODULEFILES_DIR}/: } + local caption=${caption/: \//: } + let i=($cols-${#caption})/2-2 + printf -- "%0.s-" $(seq 1 $i) 1>&2 + printf -- " %s " "${caption}" 1>&2 + printf -- "%0.s-" $(seq 1 $i) 1>&2 + printf -- "\n" 1>&2 + } + + terse_output() { + output_header + for (( i=0; i<${#mods[@]}; i+=2 )); do + local mod=${mods[i]} + local release=${mods[i+1]} + case $release in + stable ) + out='' + ;; + * ) + out="${release}" + ;; + esac + printf "%-20s\t%s\n" "${mod}" "${out}" 1>&2 + done + std::info "" + } + + # + # :FIXME: for the time being, this is the same as terse_output! + long_output() { + output_header + for (( i=0; i<${#mods[@]}; i+=2 )); do + local mod=${mods[i]} + local release=${mods[i+1]} + case $release in + stable ) + out='' + ;; + * ) + out=${release} + ;; + esac + printf "%-20s\t%s\n" "${mod}" "${out}" 1>&2 + done + std::info "" + } + + human_readable_output() { + output_header + + local -i column=$cols + local -i colsize=16 + for ((i=0; i<${#mods[@]}; i+=2)); do + if [[ ${verbosity_lvl} == 'verbose' ]]; then + local release=${mods[i+1]} + case ${mods[i+1]} in + stable ) + mod=${mods[i]} + ;; + * ) + mod="${mods[i]}(${release:0:1})" + ;; + esac + else + mod=${mods[i]} + fi + local -i len=${#mod} + local -i span=$(( len / 16 + 1 )) + local -i colsize=$(( span * 16 )) + if (( column+len >= cols )); then + printf -- "\n" 1>&2 + column=0 + fi + if (( column+colsize < cols )); then + fmt="%-${colsize}s" + else + fmt="%-s" + fi + printf "${fmt}" "${mod}" 1>&2 + column+=colsize + done + printf -- "\n\n" 1>&2 + } + local opts='' + opts=$(pmodules::get_options -o hlt -l human -l long -l terse -- "$@") || subcommand_help_avail + eval set -- "${opts}" + local pattern=() + local output_function='' + local opts='' + while (($# > 0)); do + case $1 in + -h | --human ) + [[ -z ${opts} ]] || \ + die 1 "${CMD} list: you cannot set both options: '$1' and '${opts}'." + opts=$1 + output_function='human_readable_output' + ;; + -l | --long ) + [[ -z ${opts} ]] || \ + die 1 "${CMD} list: you cannot set both options: '$1' and '${opts}'." + opts=$1 + output_function='long_output' + ;; + -t | --terse ) + [[ -z ${opts} ]] || \ + die 1 "${CMD} list: you cannot set both options: '$1' and '${opts}'." + opts=$1 + output_function='terse_output' + ;; + -- ) + ;; + * ) + pattern+=( "$1" ) + ;; + esac + shift + done + output_function=${output_function:-human_readable_output} + if (( ${#pattern[@]} == 0 )); then + pattern+=( '' ) + fi + local saved_IFS=${IFS}; + IFS=':' + local -a modulepath=(${MODULEPATH}) + IFS=${saved_IFS} + for string in "${pattern[@]}"; do + for dir in "${modulepath[@]}"; do + mods=( $( get_available_modules "${dir}" "${string}" ) ) + [[ ${#mods[@]} == 0 ]] && continue + + ${output_function} + done + done +} + +# get available groups +# $1: root of modulefile hierarchy +# +get_groups () { + local -r root="$1" + { + cd "${root}" + # for some unknown reason [A-Z]* doesn't work on (some?) SL6 systems + for f in [ABCDEFGHIJKLMNOPQRSTUVWXYZ]*; do + Groups+=( $f ) + done + }; +} + +# +# $1: root of modulefile hierarchy +get_hierarchy_depth () { + local -r root="$1" + { + cd "${root}" + local _group + for _group in "${Groups[@]}"; do + local tmp=$(find "${_group}/${PMODULES_MODULEFILES_DIR}" -depth -type f -o -type l | head -1) + local -a tmp2=( ${tmp//\// } ) + local depth=${#tmp2[@]} + let depth-=4 + HierarchyDepths[$_group]=${depth} + done + }; +} + +# +# use [-a|--append|-p|--prepend] [directory|group|release...] +# +subcommand_use() { + if (( ${#Groups[@]} == 0 )); then + get_groups "${PMODULES_ROOT}" + get_hierarchy_depth "${PMODULES_ROOT}" + fi + local saved_IFS=${IFS}; + IFS=':' + local -a modulepath=(${MODULEPATH}) + IFS=${saved_IFS} + + print_info() { + local f + local r + std::info "Used groups:" + for f in ${used_groups//:/ }; do + std::info "\t${f}" + done + std::info "\nUnused groups:" + local _group + for _group in "${Groups[@]}"; do + local -i depth=${HierarchyDepths[${_group}]} + if ! is_used_group "${_group}" && (( depth == 0 )); then + std::info "\t${_group}" + fi + done + + std::info "\nUsed releases:" + for r in ${UsedReleases//:/ }; do + std::info "\t${r}" + done + std::info "\nUnused releases:" + for r in ${PMODULES_DEFINED_RELEASES//:/ }; do + if ! is_used_release $r; then + std::info "\t${r}" + fi + done + + std::info "\nAdditonal directories in MODULEPATH:" + let n=0 + for (( i=0; i<${#modulepath[@]}; i++)); do + if [[ ! ${modulepath[i]} =~ ${PMODULES_ROOT} ]]; then + std::info "\t${modulepath[i]}" + let n+=1 + fi + done + if (( n == 0 )); then + std::info "\tnone" + fi + std::info "\n" + } + + use () { + + local dirs_to_add=() + local subcommand_switches='' + while (( $# > 0)); do + arg=$1 + # if is release + # ... + # elif is group + # ... + # elif matches modulepath root + # ... + # elif is directory + # ... + local modulefiles_dir="${PMODULES_ROOT}/${arg}/${PMODULES_MODULEFILES_DIR}" + if [[ ${arg} == -a ]] || [[ ${arg} == --append ]]; then + subcommand_switches='--append' + elif [[ ${arg} == -p ]] || [[ ${arg} == --prepend ]]; then + subcommand_switches='' + elif is_release "${arg}"; then + # releases are always *appended* + std::append_path UsedReleases "${arg}" + elif [[ ! ${arg} =~ */* ]] && [[ -d ${modulefiles_dir} ]]; then + if (( ${HierarchyDepths[$arg]} != 0 )); then + die 3 "${CMD} ${0##_}: cannot add group ${arg} to module path" + fi + std::append_path PMODULES_USED_GROUPS "${arg}" + dirs_to_add+=( ${modulefiles_dir} ) + elif [[ ${arg} =~ ^${PMODULES_ROOT} ]]; then + die 3 "${CMD} ${0##_}: illegal directory: ${arg}" + elif [[ -d ${arg} ]]; then + local normalized_dir=$(cd "${arg}" && pwd) + dirs_to_add+=( ${normalized_dir} ) + elif [[ ${arg} =~ "-*" ]]; then + die 3 "${CMD} ${0##_}: illegal switch: ${arg}" + else + die 3 "${CMD} ${0##_}: neither a directory, release or group: ${arg}" + fi + shift + done + #echo "export PMODULES_USED_GROUPS=${PMODULES_USED_GROUPS}" + declare -g PMODULES_USED_GROUPS="${PMODULES_USED_GROUPS}" + export_env PMODULES_USED_GROUPS + [[ ${#dirs_to_add[@]} == 0 ]] && return + + for dir in "${dirs_to_add[@]}"; do + subcommand_generic1 use ${subcommand_switches} "${dir}" + done + } + + if [[ $# == 0 ]]; then + std::print_info + else + use "$@" + fi +} + +# +# unuse directory|group|release... +# +subcommand_unuse() { + local opts='' + opts=$(pmodules::get_options -- '' "$@") || subcommand_help_unuse + eval set -- "${opts}" + local dirs_to_remove=() + while (( $# > 0)); do + if [[ "$1" == "--" ]]; then + shift + continue + fi + arg=$1 + # 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} =~ */* ]] && [[ -d ${modulefiles_dir} ]]; then + if (( ${HierarchyDepths[$arg]} != 0 )); then + die 3 "${CMD} ${0##_}: cannot remove group ${arg} from module path" + fi + std::remove_path PMODULES_USED_GROUPS "${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 + die 3 "${CMD} ${0##_}: illegal directory: ${arg}" + elif [[ ${arg} =~ "-*" ]]; then + die 3 "${CMD} ${0##*_}: illegal option: ${arg}" + else + die 3 "${CMD} ${0##*_}: not a directory: ${arg}" + fi + shift + done + #echo "export PMODULES_USED_GROUPS=${PMODULES_USED_GROUPS}" + declare -g PMODULES_USED_GROUPS="${PMODULES_USED_GROUPS}" + export_env PMODULES_USED_GROUPS + [[ ${#dirs_to_remove[@]} == 0 ]] && return + for dir in "${dirs_to_remove[@]}"; do + subcommand_generic1 unuse "${dir}" + done +} + +# +# update +# +# :FIXME: either compile Modules with --enable-beginenv or remove the sub-command +# +subcommand_update() { + subcommand_generic0 update "$@" +} + +# +# refresh +# +subcommand_refresh() { + subcommand_generic0 refresh "$@" +} + +# +# purge +# +subcommand_purge() { + subcommand_generic0 purge "$@" +} + +# +# list [-hlt] +# +subcommand_list() { + local opts='' + opts=$(pmodules::get_options -o hlt -l human -l long -l terse -- "$@") || subcommand_help_list + eval set -- "${opts}" + local opts='' + while (( $# > 0 )); do + case $1 in + -h | --human ) + [[ -z ${opts} ]] || \ + die 1 "${CMD} list: you cannot set both options: '$1' and '${opts}'." + opts='-h' + ;; + -l | --long ) + [[ -z ${opts} ]] || \ + die 1 "${CMD} list: you cannot set both options: '$1' and '${opts}'." + opts='-l' + ;; + -t | --terse ) + [[ -z ${opts} ]] || \ + die 1 "${CMD} list: you cannot set both options: '$1' and '${opts}'." + opts='-t' + ;; + -- ) + ;; + * ) + die 1 "${CMD} list: invalid argument -- $1" + ;; + esac + shift + done + "${modulecmd}" "${shell}" list "${opts}" +} + +pmodules_init() { + declare -g PMODULES_DEFAULT_GROUPS='' + declare -g PMODULES_DEFAULT_RELEASES='' + + source "${PMODULES_ROOT}/${PMODULES_CONFIG_DIR}/environment.bash" + + declare -g LOADEDMODULES='' + declare -g PMODULES_USED_GROUPS='' + declare -g MODULEPATH='' + declare -g _LMFILES_='' + + for group in ${PMODULES_DEFAULT_GROUPS}; do + std::append_path MODULEPATH "${PMODULES_ROOT}/${group}/${PMODULES_MODULEFILES_DIR}" + std::append_path PMODULES_USED_GROUPS "${group}" + done + declare -ag Groups='()' + declare -Ag HierarchyDepths='()' + declare -g UsedReleases='' + for r in ${PMODULES_DEFAULT_RELEASES//:/ }; do + std::append_path UsedReleases "${r}" + done + +} + +# +# clear +# +subcommand_clear() { + local opts='' + opts=$(pmodules::get_options -- '' "$@") || subcommand_help_${subcommand} + eval set -- "${opts}" + while (( $# > 0 )); do + case $1 in + -- ) + shift + ;; + * ) + die 3 "${CMD} ${subcommand}: illegal argument -- $1" + ;; + esac + done + pmodules_init + export_env LOADEDMODULES PMODULES_USED_GROUPS MODULEPATH _LMFILES_ +} + +# +# search [switches] [STRING...] +# +subcommand_search() { + local modules=() + local with_modules='//' + local src_prefix='' + local _print_header='yes' + local _print_modulefiles='no' + local 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 + } + + # args: + # $1: module name pattern + search () { + local -r module=$1 + # we must write temporary results to a file for sorting + local -r tmpfile=$( mktemp /tmp/$(basename $0).XXXXXX ) \ + || die 1 "Oops: unable to create tmp file!" + local _group + # loop over all groups + for _group in "${Groups[@]}"; do + local depth=${HierarchyDepths[${_group}]} + # get all potential directories of group with module-files + local mpaths=( $(find \ + "${src_prefix}/${_group}/modulefiles" \ + -type d \ + -mindepth ${depth} -maxdepth ${depth} \ + 2>/dev/null)) + local mpath + for mpath in "${mpaths[@]}"; do + # get dependencies encoded in directory name + local p="${mpath/${src_prefix}}" + p=( ${p//\// } ) + local deps=() + local -i i + for ((i=2; i < ${#p[@]}; i+=2)); do + deps+=( ${p[i]}/${p[i+1]} ) + done + local requires=${deps[@]} + + # get and print all available modules in $mpath + # with respect to the requested releases + local mods=( $( get_available_modules \ + "${mpath}" \ + "${module}" \ + "${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 + if [[ "${_print_modulefiles}" == "no" ]]; then + sort -k 1,1 -k 4,4 -k 5,5 "${tmpfile}" | awk "${with_modules}" 1>&2 + else + 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}" + done < <(sort -k 1,1 -k 4,4 -k 5,5 "${tmpfile}" | awk "${with_modules}") + fi + rm -f "${tmpfile}" + } + + opts=$(pmodules::get_options -o 'ahH?' \ + -l help \ + -l no-header \ + -l print-modulefiles \ + -l release: \ + -l with: \ + -l all-releases \ + -l src: \ + -- "$@") || subcommand_help_${0##*_} + eval set -- "${opts}" + + while (( $# > 0 )); do + case $1 in + --no-header ) + _print_header='no' + ;; + --print-modulefiles ) + _print_modulefiles='yes' + _print_header='no' + ;; + --release ) + is_release "$2" || \ + die 1 "${CMD} search: illegal release name -- $2" + use_releases+="$2:" + shift + ;; + --with ) + if [[ -z $2 ]] || [[ "$2" =~ "-*" ]]; then + die 1 "${CMD} search: with what?" + fi + with_modules+=" && / ${2//\//\\/}/" + shift + ;; + -a | --all-releases ) + use_releases="${PMODULES_DEFINED_RELEASES}" + ;; + --src ) + src_prefix=$2 + pmodules::check_directories "${src_prefix}" + + shift + ;; + -\? | -h | -H | --help ) + usage + ;; + -- ) + ;; + * ) + modules+=( "$1" ) + ;; + esac + shift + done + if [[ -z "${src_prefix}" ]]; then + src_prefix="${PMODULES_ROOT}" + fi + + if [[ "${use_releases}" == ":" ]]; then + use_releases=":${UsedReleases}:" + fi + + [[ "${_print_header}" == "yes" ]] && print_header + if [[ ${#modules[@]} == 0 ]]; then + modules+=( '' ) + fi + + if (( ${#Groups[@]} == 0 )) || [[ ${src_prefix} != ${PMODULES_ROOT} ]]; then + get_groups "${src_prefix}" + get_hierarchy_depth "${src_prefix}" + fi + + for module in "${modules[@]}"; do + search "${module}" + done +} + +# +# help [module|sub-command] +# +subcommand_help() { + local opts='' + opts=$(pmodules::get_options -- '' "$@") || usage + eval set -- "${opts}" + local arg='' + + while (( $# > 0 )); do + case $1 in + -- ) + : + ;; + * ) + [[ -z ${arg} ]] || \ + die 1 "${CMD} help: only one argument allowed." + arg="$1" + ;; + esac + shift + done + if [[ -z ${arg} ]]; then + usage + elif typeset -F subcommand_help_${arg} > /dev/null 2>&1 ; then + # help for sub-command + subcommand_help_${arg} + else + # :FIXME: print help of newest *available* module + # (respecting UsedReleases) + subcommand_generic1plus help "${arg}" + fi +} + +# +# whatis [module] +# +subcommand_whatis() { + if (( $# == 0 )); then + subcommand_generic0 whatis + else + subcommand_generic1plus whatis "$@" + fi +} + +# +# apropos string +# +subcommand_apropos() { + subcommand_generic1 apropos "$@" +} + +# +# initadd module... +# +subcommand_initadd() { + subcommand_generic1plus initadd "$@" +} + +# +# initprepend module... +# +subcommand_initprepend() { + subcommand_generic1plus initprepend "$@" +} + +# +# initrm module... +# +subcommand_initrm() { + subcommand_generic1plus initrm "$@" +} + +# +# initswitch module1 module2 +# +subcommand_initswitch() { + subcommand_generic1or2 initswitch "$@" +} + +# +# initlist +# +subcommand_initlist() { + subcommand_generic0 initlist "$@" +} + +# +# initclear +# +subcommand_initclear() { + subcommand_generic0 initclear "$@" +} + +if [[ -n ${PMODULES_ENV} ]]; then + eval "$("${PMODULES_HOME}/bin/base64" -d <<< "${PMODULES_ENV}" 2>/dev/null)" +else + pmodules_init +fi + +case $1 in + bash ) + declare shell="$1" + ;; + tcsh ) + declare shell="$1" + ;; + * ) + die 1 "${CMD}: unsupported shell -- $1" + ;; +esac +shift + +declare -a opts=() +while (( $# > 0 )); do + case $1 in + -H | -\? | --help | -help ) + usage + ;; + -V | --version ) + print_version + die 1 + ;; + -* ) + opts+=( "$1" ) + ;; + add|load ) + subcommand='subcommand_load' + shift + opts+=( "$@" ) + shift $# + ;; + rm|unload ) + subcommand='subcommand_unload' + shift + opts+=( "$@" ) + shift $# + ;; + switch|swap ) + subcommand='subcommand_swap' + shift + opts+=( "$@" ) + shift $# + ;; + display|show ) + subcommand='subcommand_show' + shift + opts+=( "$@" ) + shift $# + ;; + apropos|keyword ) + subcommand='subcommand_apropos' + shift + opts+=( "$@" ) + shift $# + ;; + avail|search|use|unuse|update|refresh|purge|list|clear|whatis|help ) + subcommand=subcommand_$1 + shift + if (( $# > 0 )); then + opts+=( "$@" ) + shift $# + fi + ;; + initadd|initprepend|initrm|initswitch|initlist|initclear ) + subcommand=subcommand_$1 + shift + opts=( "$@" ) + shift $# + ;; + * ) + die 1 "${CMD}: unknown sub-command -- $1" + ;; + esac + shift +done + +if (( ${#Groups[@]} == 0 )); then + get_groups "${PMODULES_ROOT}" + get_hierarchy_depth "${PMODULES_ROOT}" +fi + +$subcommand "${opts[@]}" + +# Local Variables: +# mode: sh +# sh-basic-offset: 8 +# tab-width: 8 +# End: diff --git a/Pmodules/profile.bash b/Pmodules/profile.bash new file mode 100644 index 0000000..77e4b6e --- /dev/null +++ b/Pmodules/profile.bash @@ -0,0 +1,59 @@ +#!/bin/bash +# +############################################################################# +# D O N O T C H A N G E T H I S F I L E ! # +############################################################################# + +############################################################################# +# read Pmodules configuration +# +_init_env_file="$(dirname ${BASH_SOURCE})/environment.bash" +if [[ ! -r "${_init_env_file}" ]]; then + echo "Oops: cannot initialize Modules!" + echo "${_init_env_file}: file does not exist or is not readable." + return 1 +fi +source "${_init_env_file}" +unset _init_env_file + +############################################################################ +# check configuration +# +if [[ -z ${PMODULES_ROOT} ]]; then + echo "Oops: Pmodules root not set." 1>&2 + return 1 +fi +if [[ ! -d ${PMODULES_ROOT} ]]; then + echo "Oops: ${PMODULES_ROOT}: Set as Pmodules root, but this is not a directory." 1>&2 + return 1 +fi +if [[ -z ${PMODULES_VERSION} ]]; then + echo "Oops: ${PMODULES_VERSION}: Pmodules version not set." 1>&2 + return 1 +fi +if [[ -z ${PMODULES_HOME} ]]; then + echo "Oops: Pmodules home not set." 1>&2 + return 1 +fi +if [[ ! -d ${PMODULES_HOME} ]]; then + echo "Oops: ${PMODULES_HOME}: Set as Pmodules home, but this is not a directory." 1>&2 + return 1 +fi + +############################################################################ +# inititialize Pmodules for bash +# +_init_bash="${PMODULES_HOME}/init/bash" +if [[ ! -r "${_init_bash}" ]]; then + echo "Oops: cannot initialize Modules!" + echo "${_init_bash}: File does not exist or is not readable." + return 1 +fi +source "${_init_bash}" +unset _init_bash + +# Local Variables: +# mode: sh +# sh-basic-offset: 8 +# tab-width: 8 +# End: diff --git a/Pmodules/profile.csh b/Pmodules/profile.csh new file mode 100755 index 0000000..0aee6f5 --- /dev/null +++ b/Pmodules/profile.csh @@ -0,0 +1,69 @@ +#!/bin/tcsh + +############################################################################# +# read Pmodules configuration +# +set _init_env_file="/opt/psi/config/environment.bash" +if ( ! -r "$_init_env_file" ); then + echo "Oops: cannot initialize Modules!" + echo "$_init_env_file: file does not exist or is not readable." + return 1 +fi +source "$_init_env_file" +unset _init_env_file + +############################################################################ +# check configuration +# +if ( ! $?PMODULES_PREFIX ) then + echo "Oops: Pmodules prefix not set." + return 1 +endif + +if ( "$PMODULES_PREFIX" == "" ) then + echo "Oops: Pmodules prefix set to empty string!" +endif +if ( ! -d ${PMODULES_PREFIX} ) then + echo "Oops: ${PMODULES_PREFIX}: Set as Pmodules prefix, but this is not a directory!" + return 1 +endif + +if ( ! $?PMODULES_VERSION ) then + echo "Oops: Pmodules version not set!" + return 1 +endif +if ( "$PMODULES_VERSION" == "" ) then + echo "Oops: Pmodules version set to empty string!" + return 1 +endif + +if ( $?PMODULES_HOME ) then + echo "Oops: Pmodules home not set!" + return 1 +endif +if ( "$PMODULES_HOME" == "" ) then + echo "Oops: Pmodules home set to empty string!" + return 1 +fi +if ( ! -d "$PMODULES_HOME" ) then + echo "Oops: $PMODULES_HOME: Set as Pmodules home, but this is not a directory!" + return 1 +endif + +############################################################################ +# inititialize Pmodules for bash +# +set _init_csh="$PMODULES_HOME/init/csh" +if ( ! -r "$_init_csh" ) then + echo "Oops: cannot initialize Modules!" + echo "$_init_csh: File does not exist or is not readable." + return 1 +fi +source "$_init_csh" +unset _init_csh + +# Local Variables: +# mode: csh +# sh-basic-offset: 8 +# tab-width: 8 +# End: diff --git a/Pmodules_version.conf b/Pmodules_version.conf new file mode 100644 index 0000000..779ebf9 --- /dev/null +++ b/Pmodules_version.conf @@ -0,0 +1,8 @@ +bash 4.3.30 +coreutils 8.23 +dialog 1.2.1 +getopt 1.1.6 +gettext 0.19.4 +Modules 3.2.10 +Pmodules 0.99.5 +Tcl 8.6.3 diff --git a/Tcl/build b/Tcl/build new file mode 100755 index 0000000..379122e --- /dev/null +++ b/Tcl/build @@ -0,0 +1,24 @@ +#!/usr/bin/env pbuild + +pmodules.configure() { + case ${OS} in + Linux ) + srcdir="${MODULE_SRCDIR}/unix" + ;; + Darwin ) + srcdir="${MODULE_SRCDIR}/macosx" + ;; + esac + "${srcdir}"/configure \ + --prefix="${PREFIX}" \ + --enable-shared=no \ + || exit 1 +} + +pmodules.post_install() { + { cd "${PREFIX}"/bin && rm -f tclsh && ln -fs tclsh${V%.*} tclsh; }; +} + +pmodules.add_to_group 'Tools' +pmodules.set_docfiles 'license.terms' 'README' +pmodules.make_all diff --git a/bash/build b/bash/build new file mode 100755 index 0000000..70cdb1a --- /dev/null +++ b/bash/build @@ -0,0 +1,10 @@ +#!/usr/bin/env pbuild + +pmodules.configure() { + "${MODULE_SRCDIR}"/configure \ + --prefix="${PREFIX}" \ + || exit 1 +} + +pmodules.add_to_group 'Tools' +pmodules.make_all diff --git a/bootstrap.sh b/bootstrap.sh new file mode 100755 index 0000000..e63c140 --- /dev/null +++ b/bootstrap.sh @@ -0,0 +1,7 @@ +#!/bin/bash +declare -r BASE_DIR=$(cd "$(dirname $0)/../.." && pwd) +declare -r BOOTSTRAP_DIR="${BASE_DIR}/Bootstrap" + +${BOOTSTRAP_DIR}/compile_pmodules.sh +${BOOTSTRAP_DIR}/install_pmodules.sh + diff --git a/compile_pmodules.sh b/compile_pmodules.sh new file mode 100755 index 0000000..00fcdb2 --- /dev/null +++ b/compile_pmodules.sh @@ -0,0 +1,54 @@ +#!/bin/bash + +declare -r BASE_DIR=$(cd "$(dirname $0)/.." && pwd) +declare -r BOOTSTRAP_DIR="${BASE_DIR}/Bootstrap" + +source "${BASE_DIR}/lib/lib.bash" + +unset PMODULES_HOME +unset PMODULES_VERSION + +read_versions "${BOOTSTRAP_DIR}/Pmodules_version.conf" +source "/opt/psi/config/environment.bash" + +#if [[ -n ${PMODULES_DIR} ]] && [[ "${PMODULES_DIR}" != "/" ]] && [[ -d "${PMODULES_DIR}" ]]; then +# rm -rf "${PMODULES_DIR}" +#fi + +build () { + local -r name="$1" + local -r version="$2" + + "${BOOTSTRAP_DIR}/${name}/build" --bootstrap --disable-cleanup "${version}" || \ + std::die 3 "Compiling '${name}' failed!" +} + +if [[ ! -f "${PMODULES_HOME}/bin/base64" ]]; then + build coreutils "${COREUTILS_VERSION}" +fi + +if [[ ! -f "${PMODULES_HOME}/bin/xgettext" ]]; then + build gettext "${GETTEXT_VERSION}" +fi + +if [[ ! -f "${PMODULES_HOME}/bin/getopt" ]]; then + build getopt "${GETOPT_VERSION}" +fi + +if [[ ! -f "${PMODULES_HOME}/bin/dialog" ]]; then + build dialog "${DIALOG_VERSION}" +fi + +if [[ ! -f "${PMODULES_HOME}/bin/bash" ]]; then + build bash "4.3.30" +fi + +if [[ ! -e "${PMODULES_HOME}/bin/tclsh" ]]; then + build Tcl "${TCL_VERSION}" +fi + +if [[ ! -e "${PMODULES_HOME}/bin/modulecmd.tcl" ]]; then + build Modules "${MODULES_VERSION}" + mv -v "${PMODULES_HOME}/bin/modulecmd" "${PMODULES_HOME}/libexec/modulecmd.tcl" +fi +echo "Done..." diff --git a/coreutils/build b/coreutils/build new file mode 100755 index 0000000..63030c7 --- /dev/null +++ b/coreutils/build @@ -0,0 +1,10 @@ +#!/usr/bin/env pbuild + +pmodules.configure() { + "${MODULE_SRCDIR}"/configure \ + --prefix="${PREFIX}" \ + || exit 1 +} + +pmodules.add_to_group 'Tools' +pmodules.make_all diff --git a/coreutils/build.new b/coreutils/build.new new file mode 100755 index 0000000..63030c7 --- /dev/null +++ b/coreutils/build.new @@ -0,0 +1,10 @@ +#!/usr/bin/env pbuild + +pmodules.configure() { + "${MODULE_SRCDIR}"/configure \ + --prefix="${PREFIX}" \ + || exit 1 +} + +pmodules.add_to_group 'Tools' +pmodules.make_all diff --git a/dialog/build b/dialog/build new file mode 100755 index 0000000..70cdb1a --- /dev/null +++ b/dialog/build @@ -0,0 +1,10 @@ +#!/usr/bin/env pbuild + +pmodules.configure() { + "${MODULE_SRCDIR}"/configure \ + --prefix="${PREFIX}" \ + || exit 1 +} + +pmodules.add_to_group 'Tools' +pmodules.make_all diff --git a/getopt/build b/getopt/build new file mode 100755 index 0000000..b7984cb --- /dev/null +++ b/getopt/build @@ -0,0 +1,40 @@ +#!/usr/bin/env pbuild + +pmodules.configure() { + : +} + +pmodules.build() { + case ${OS} in + Linux ) + declare -x LDFLAGS="-lintl" + ;; + Darwin ) + declare -x LDFLAGS="-lintl -liconv -framework CoreFoundation" + ;; + esac + + cd "${MODULE_SRCDIR}" + make -e +} + +pmodules.install() { + cd "${MODULE_SRCDIR}" + declare -x DESTDIR="${PREFIX}" + declare -x prefix='' + make -e install +} + +pmodules.cleanup_build() { + cd "${MODULE_SRCDIR}" + make -e realclean +} + +pmodules.add_to_group 'Tools' +pmodules.make_all + +# Local Variables: +# mode: sh +# sh-basic-offset: 8 +# tab-width: 8 +# End: diff --git a/gettext/build b/gettext/build new file mode 100755 index 0000000..ad631df --- /dev/null +++ b/gettext/build @@ -0,0 +1,22 @@ +#!/usr/bin/env pbuild + +pmodules.configure() { + "${MODULE_SRCDIR}"/configure \ + --prefix="${PREFIX}" \ + --disable-java \ + --disable-threads \ + --disable-shared \ + --enable-relocatable \ + --disable-openmp \ + --disable-acl \ + --disable-curses \ + --with-included-gettext \ + --without-libiconv-prefix \ + --without-libintl-prefix \ + --with-included-libxml \ + --with-pic=yes \ + || exit 1 +} + +pmodules.add_to_group 'Tools' +pmodules.make_all diff --git a/install_pmodules.sh b/install_pmodules.sh new file mode 100755 index 0000000..d6965c4 --- /dev/null +++ b/install_pmodules.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +declare -r BASE_DIR=$(cd "$(dirname $0)/.." && pwd) +declare -r BOOTSTRAP_DIR="${BASE_DIR}/Bootstrap" +declare -r SRC_DIR="${BOOTSTRAP_DIR}/Pmodules" + +source "${BASE_DIR}/lib/lib.bash" + +unset PMODULES_HOME +unset PMODULES_VERSION + +read_versions "${BOOTSTRAP_DIR}/Pmodules_version.conf" +source "/opt/psi/config/environment.bash" + +echo "Installing to ${PMODULES_HOME} ..." +sed_cmd="s:@PMODULES_HOME@:${PMODULES_HOME}:g;" +sed_cmd+="s:@PMODULES_VERSION@:${PMODULES_VERSION}:g;" +sed_cmd+="s:@MODULES_VERSION@:${MODULES_VERSION}:g" +sed "${sed_cmd}" "${SRC_DIR}/modulecmd.bash.in" > "${SRC_DIR}/modulecmd.bash" +sed "${sed_cmd}" "${SRC_DIR}/modmanage.bash.in" > "${SRC_DIR}/modmanage.bash" +sed "${sed_cmd}" "${SRC_DIR}/environment.bash.in" > "${SRC_DIR}/environment.bash" + +install -d -m 0755 "${PMODULES_HOME}/bin" +install -d -m 0755 "${PMODULES_HOME}/config" +install -d -m 0755 "${PMODULES_HOME}/init" +install -d -m 0755 "${PMODULES_HOME}/lib" + +install -m 0755 "${SRC_DIR}/modulecmd" "${PMODULES_HOME}/bin" +install -m 0755 "${SRC_DIR}/modulecmd.bash" "${PMODULES_HOME}/libexec" +install -m 0755 "${SRC_DIR}/modmanage" "${PMODULES_HOME}/bin" +install -m 0755 "${SRC_DIR}/modmanage.bash" "${PMODULES_HOME}/libexec" +install -m 0755 "${SRC_DIR}/dialog.bash" "${PMODULES_HOME}/bin" + +install -m 0755 "${SRC_DIR}/environment.bash" "${PMODULES_HOME}/config" +install -m 0755 "${SRC_DIR}/profile.bash" "${PMODULES_HOME}/config" + +install -m 0644 "${SRC_DIR}/bash" "${PMODULES_HOME}/init" +install -m 0644 "${SRC_DIR}/bash_completion" "${PMODULES_HOME}/init" + +install -m 0644 "${SRC_DIR}/libpmodules.bash" "${PMODULES_HOME}/lib" +install -m 0644 "${SRC_DIR}/libmodules.tcl" "${PMODULES_HOME}/lib/tcl8.6" + +{ + cd "${PMODULES_HOME}/lib/tcl8.6" + "${BOOTSTRAP_DIR}/mkindex.tcl" +} diff --git a/mkindex.tcl b/mkindex.tcl new file mode 100755 index 0000000..297872f --- /dev/null +++ b/mkindex.tcl @@ -0,0 +1,3 @@ +#!/usr/bin/env tclsh + +auto_mkindex . *.tcl