From 0a1d81946b43cdd1e3e2f1157fdb3416e8b04aee Mon Sep 17 00:00:00 2001 From: Achim Gsell Date: Wed, 24 Jul 2024 16:53:13 +0200 Subject: [PATCH] modulecmd & build-system: code review with shellcheck --- Pmodules/Pmodules.py | 1 - Pmodules/libmodules.tcl | 37 +- Pmodules/libpbuild.bash | 280 +++++------ Pmodules/libpmodules.bash.in | 385 ++++++++------- Pmodules/libstd.bash | 223 +++------ Pmodules/modbuild.in | 437 +++++------------ Pmodules/modmanage.bash.in | 747 ---------------------------- Pmodules/modmanage.in | 11 - Pmodules/modulecmd.bash.in | 925 ++++++++++++++++++----------------- build | 64 ++- config/Pmodules.yaml.in | 2 - recipes/120-7z | 2 +- 12 files changed, 1058 insertions(+), 2056 deletions(-) delete mode 100755 Pmodules/modmanage.bash.in delete mode 100644 Pmodules/modmanage.in diff --git a/Pmodules/Pmodules.py b/Pmodules/Pmodules.py index 68993e5..4f16601 100644 --- a/Pmodules/Pmodules.py +++ b/Pmodules/Pmodules.py @@ -2,7 +2,6 @@ import os, re, subprocess def module(*args): os.environ['PMODULES_MODULEFILES_DIR']='modulefiles' - os.environ['PMODULES_CONFIG_DIR']='config' pm_home=os.environ['PMODULES_HOME'] os.environ['PMODULES_DIR']=pm_home modulecmd=os.path.join(pm_home, 'bin', 'modulecmd') diff --git a/Pmodules/libmodules.tcl b/Pmodules/libmodules.tcl index 7f81026..6573fde 100644 --- a/Pmodules/libmodules.tcl +++ b/Pmodules/libmodules.tcl @@ -4,7 +4,6 @@ # unload modules if parent removed # - if {[info exists env(PMODULES_DEBUG)] && $env(PMODULES_DEBUG)} { proc debug {msg} { set level [expr [info level] -2] @@ -92,7 +91,7 @@ proc module-addgroup { group } { debug "group=$group" debug "::variant=$::variant" set dir [file join \ - $::OverlayInfo($overlay:mod_root) \ + $::OverlayInfo($overlay:modulefiles_root) \ $group \ $::MODULEFILES_DIR \ {*}$::variant] @@ -126,7 +125,7 @@ proc module-addgroup { group } { debug "mode=remove: $env(MODULEPATH)" foreach overlay $::UsedOverlays { set dir [file join \ - $::OverlayInfo($overlay:mod_root) \ + $::OverlayInfo($overlay:modulefiles_root) \ $group \ $::MODULEFILES_DIR \ {*}$::variant] @@ -288,20 +287,20 @@ proc _find_overlay { modulefile_components } { debug "_find_overlay()" foreach ol $::UsedOverlays { debug "ol = $ol" - set ol_mod_root $::OverlayInfo(${ol}:mod_root) - if { [string range $ol_mod_root end end] == "/" } { - set ol_mod_root [string range $ol_mod_root 0 end-1] + set ol_modulefiles_root $::OverlayInfo(${ol}:modulefiles_root) + if { [string range $ol_modulefiles_root end end] == "/" } { + set ol_modulefiles_root [string range $ol_modulefiles_root 0 end-1] } - debug "ol_mod_root = $ol_mod_root" - set ol_mod_root_splitted [file split $ol_mod_root] + debug "ol_modulefiles_root = $ol_modulefiles_root" + set ol_modulefiles_root_splitted [file split $ol_modulefiles_root] set modulefile_root [file join \ {*}[lrange \ $modulefile_components \ - 0 [expr [llength $ol_mod_root_splitted] - 1]]] + 0 [expr [llength $ol_modulefiles_root_splitted] - 1]]] debug "modulefile_root = $modulefile_root" - if { [string compare $ol_mod_root $modulefile_root] == 0 } { - debug "ol_mod_root_splitted = $ol_mod_root_splitted" - return $ol_mod_root_splitted + if { [string compare $ol_modulefiles_root $modulefile_root] == 0 } { + debug "ol_modulefiles_root_splitted = $ol_modulefiles_root_splitted" + return $ol_modulefiles_root_splitted } } debug "overlay not found" @@ -333,28 +332,28 @@ proc _pmodules_init_global_vars { } { set modulefile_splitted [file split $::ModulesCurrentModulefile] - set ol_mod_root_splitted [_find_overlay ${modulefile_splitted}] - set rel_modulefile [lrange $modulefile_splitted [llength $ol_mod_root_splitted] end] + set ol_modulefiles_root_splitted [_find_overlay ${modulefile_splitted}] + debug "init: ol_modulefiles_root_splitted=$ol_modulefiles_root_splitted" + set rel_modulefile [lrange $modulefile_splitted [llength $ol_modulefiles_root_splitted] end] set group [lindex $rel_modulefile 0] set GROUP "${group}" set name [lindex $modulefile_splitted end-1] set version [lindex $modulefile_splitted end] - set suffixes [lassign [split $version _] v] lassign [split $v -] V_PKG V_RELEASE lassign [split $V_PKG .] V_MAJOR V_MINOR V_PATCHLVL set variant [lrange $rel_modulefile 2 end] - set mod_root [file join {*}$ol_mod_root_splitted] - set ol $::Dir2OverlayMap($mod_root) + set modulefiles_root [file join {*}$ol_modulefiles_root_splitted] + set ol $::Dir2OverlayMap($modulefiles_root) - set install_prefix [file split $::OverlayInfo(${ol}:inst_root)] + set install_prefix [file split $::OverlayInfo(${ol}:install_root)] set prefix "$install_prefix $group [lreverse_n $variant 2]" set PREFIX [file join {*}$prefix] set P "${name}" set V "${version}" - debug "mod_root=$mod_root" + debug "modulefiles_root=$modulefiles_root" debug "ol=$ol" debug "PREFIX=$PREFIX" debug "group of module $name: $group" diff --git a/Pmodules/libpbuild.bash b/Pmodules/libpbuild.bash index 4c6489c..41e2d70 100644 --- a/Pmodules/libpbuild.bash +++ b/Pmodules/libpbuild.bash @@ -6,10 +6,8 @@ unset CDPATH #............................................................................. # define constants -declare -r BNAME_VARIANTS='variants' declare -r FNAME_RDEPS='.dependencies' declare -r FNAME_IDEPS='.install_dependencies' -declare -r FNAME_BDEPS='.build_dependencies' # relative path of documentation # abs. path is "${PREFIX}/${_docdir}/${module_name}" @@ -24,7 +22,7 @@ declare -A SOURCE_UNPACK_DIRS=() declare -ax CONFIGURE_ARGS=() declare -a PATCH_FILES=() declare -a PATCH_STRIPS=() -declare -a PATCH_STRIP_DEFAULT='1' +declare -- PATCH_STRIP_DEFAULT='1' declare -- configure_with='auto' declare -- SRC_DIR='' declare -- BUILD_DIR='' @@ -32,6 +30,9 @@ declare -- is_subpkg='no' declare -i group_depth=0 +declare -- COMPILER='' +declare -- MPI='' + #............................................................................. # # Exit script on errror. @@ -54,7 +55,7 @@ trap "_error_handler" ERR # write number of cores to stdout # _get_num_cores() { - case "${OS}" in + case "${KernelName}" in Linux ) ${grep} -c ^processor /proc/cpuinfo ;; @@ -62,7 +63,7 @@ _get_num_cores() { ${sysctl} -n hw.ncpu ;; * ) - std::die 1 "OS ${OS} is not supported\n" + std::die 1 "OS ${KernelName} is not supported\n" ;; esac } @@ -163,17 +164,15 @@ pbuild::add_to_group() { if (( $# == 0 )); then std::die 42 \ "%s " "${module_name}/${module_version}:" \ - "${FUNCNAME}: missing group argument." + "${FUNCNAME[0]}: missing group argument." fi if (( $# > 1 )); then std::die 42 \ "%s " "${module_name}/${module_version}:" \ - "${FUNCNAME}: only one argument is allowed." - fi - if [[ ${opt_yaml} == 'yes' ]]; then - std::info \ - "Using ${FUNCNAME} is deprecated with YAML module configuration files." + "${FUNCNAME[0]}: only one argument is allowed." fi + std::info \ + "Using ${FUNCNAME[0]} is deprecated with YAML module configuration files." pbuild.add_to_group "$@" } readonly -f pbuild::add_to_group @@ -201,18 +200,17 @@ readonly -f pbuild.add_to_group # 1 otherwise # pbuild::module_is_avail() { - local output=( $("${MODULECMD}" bash avail -a -m "$1" \ - 2>&1 1>/dev/null) ) - local i - for (( i = 0; i < ${#output[@]}; i += 2 )); do - if [[ "${output[$i]}" == "$1" ]]; then + local -- name='' + local -- release='' + while read -r name release; do + if [[ "${name}" == "$1" ]]; then if (( $# > 1 )); then local -n _result="$2" - _result="${output[i+1]}" + _result="${release}" fi return 0 fi - done + done < <(${modulecmd} bash avail -a -m "$1" 2>&1 1>/dev/null) return 1 } readonly -f pbuild::module_is_avail @@ -252,17 +250,19 @@ pbuild::version_compare () { [[ $1 =~ ^[0-9]+$ ]] } - [[ $1 == $2 ]] && return 0 - local IFS=. - local i ver1=($1) ver2=($2) + [[ "$1" == "$2" ]] && return 0 + local ver1 ver2 + IFS='.' read -r -a ver1 <<<"$1" + IFS='.' read -r -a ver2 <<<"$2" # fill empty fields in ver1 with zeros + local i for ((i=${#ver1[@]}; i<${#ver2[@]}; i++)); do ver1[i]=0 done for ((i=0; i<${#ver1[@]}; i++)); do [[ -z ${ver2[i]} ]] && ver2[i]=0 - if is_uint ${ver1[i]} && is_uint ${ver2[i]}; then + if is_uint "${ver1[i]}" && is_uint "${ver2[i]}"; then ((10#${ver1[i]} > 10#${ver2[i]})) && return 1 ((10#${ver1[i]} < 10#${ver2[i]})) && return 2 else @@ -352,9 +352,8 @@ readonly -f pbuild::version_eq # Default is all. # pbuild::supported_compilers() { - [[ ${opt_yaml} == 'yes' ]] && \ - std::info \ - "Using ${FUNCNAME} is deprecated with YAML module configuration files." + std::info \ + "Using ${FUNCNAME[0]} is deprecated with YAML module configuration files." pbuild.supported_compilers "$@" } readonly -f pbuild::supported_compilers @@ -374,9 +373,8 @@ readonly -f pbuild.supported_compilers # Default is all. # pbuild::supported_systems() { - [[ ${opt_yaml} == 'yes' ]] && \ - std::info \ - "Using ${FUNCNAME} is deprecated with YAML module configuration files." + std::info \ + "Using ${FUNCNAME[0]} is deprecated with YAML module configuration files." pbuild.supported_systems "$@" } readonly -f pbuild::supported_systems @@ -390,7 +388,7 @@ readonly -f pbuild.supported_systems #.............................................................................. # pbuild::use_flag() { - [[ "${USE_FLAGS}" =~ ":${1}:" ]] + [[ "${USE_FLAGS}" == *:${1}:* ]] } readonly -f pbuild::use_flag @@ -406,16 +404,14 @@ readonly -f pbuild::use_flag # $1 download URL # $2 optional file-name (of) pbuild::set_download_url() { - if [[ ${opt_yaml} == 'yes' ]]; then - std::info \ - "Using ${FUNCNAME} is deprecated with YAML module configuration files." - fi + std::info \ + "Using ${FUNCNAME[0]} is deprecated with YAML module configuration files." local -i _i=${#SOURCE_URLS[@]} - SOURCE_URLS[_i]=$1 + SOURCE_URLS[_i]="$1" if (( $# > 1 )); then - SOURCE_NAMES[$_i]=${2:-${1##*/}} + SOURCE_NAMES[_i]="${2:-${1##*/}}" else - SOURCE_NAMES[$_i]="${1##*/}" + SOURCE_NAMES[_i]="${1##*/}" fi SOURCE_STRIP_DIRS[_i]='1' } @@ -424,7 +420,7 @@ readonly -f pbuild::set_download_url pbuild.set_urls(){ local -i _i=${#SOURCE_URLS[@]} SOURCE_URLS[_i]="$1" - SOURCE_NAMES[$_i]="$2" + SOURCE_NAMES[_i]="$2" SOURCE_STRIP_DIRS[_i]="$3" SOURCE_UNPACKER[_i]="$4" } @@ -440,9 +436,8 @@ pbuild.set_urls(){ # Maybe we should use a dictionary in the future. # pbuild::set_sha256sum() { - [[ ${opt_yaml} == 'yes' ]] && \ - std::info \ - "Using ${FUNCNAME} is deprecated with YAML module configuration files." + std::info \ + "Using ${FUNCNAME[0]} is deprecated with YAML module configuration files." SOURCE_SHA256_SUMS+=("$1") } readonly -f pbuild::set_sha256sum @@ -463,13 +458,12 @@ readonly -f pbuild::set_unpack_dir #.............................................................................. # pbuild::add_patch() { - [[ ${opt_yaml} == 'yes' ]] && \ - std::info \ - "Using ${FUNCNAME} is deprecated with YAML module configuration files." + std::info \ + "Using ${FUNCNAME[0]} is deprecated with YAML module configuration files." [[ -z "$1" ]] && \ std::die 1 \ "%s " "${module_name}/${module_version}:" \ - "${FUNCNAME}: missing argument!" + "${FUNCNAME[0]}: missing argument!" PATCH_FILES+=( "$1" ) if (( $# >= 2 )); then PATCH_STRIPS+=( "$2" ) @@ -480,9 +474,8 @@ pbuild::add_patch() { readonly -f pbuild::add_patch pbuild.add_patch_files(){ - local -a args="$@" local -- arg='' - for arg in "${args[@]}"; do + for arg in "$@"; do [[ -z "${arg}" ]] && continue if [[ ${arg} == *:* ]]; then PATCH_FILES+=( "${arg%%:*}" ) @@ -498,13 +491,12 @@ readonly -f pbuild.add_patch_files #.............................................................................. # pbuild::set_default_patch_strip() { - [[ ${opt_yaml} == 'yes' ]] && \ - std::info \ - "Using ${FUNCNAME} is deprecated with YAML module configuration files." + std::info \ + "Using ${FUNCNAME[0]} is deprecated with YAML module configuration files." [[ -n "$1" ]] || \ std::die 1 \ "%s " "${module_name}/${module_version}:" \ - "${FUNCNAME}: missing argument!" + "${FUNCNAME[0]}: missing argument!" PATCH_STRIP_DEFAULT="$1" } @@ -606,8 +598,7 @@ pbuild::prep() { done if [[ "${dir}" == 'not found' ]]; then dir="${dirs[0]}" - download_with_curl "${dir}/${fname}" "${url}" - (( $? == 0 )) || \ + download_with_curl "${dir}/${fname}" "${url}" || \ std::die 42 \ "%s " \ "${module_name}/${module_version}:" \ @@ -675,12 +666,6 @@ pbuild::prep() { "error patching sources!" done } - for fname in ${VERSIONS[@]/#/pbuild::set_download_url_}; do - if typeset -F ${fname} 2>/dev/null; then - $f - break - fi - done (( ${#SOURCE_URLS[@]} == 0 )) && return 0 ${mkdir} -p "${PMODULES_DISTFILESDIR}" local i=0 @@ -718,9 +703,8 @@ pbuild::prep() { #.............................................................................. # pbuild::add_configure_args() { - [[ ${opt_yaml} == 'yes' ]] && \ - std::info \ - "Using ${FUNCNAME} is deprecated with YAML module configuration files." + std::info \ + "Using ${FUNCNAME[0]} is deprecated with YAML module configuration files." CONFIGURE_ARGS+=( "$@" ) } readonly -f pbuild::add_configure_args @@ -733,9 +717,8 @@ readonly -f pbuild.add_configure_args #.............................................................................. # pbuild::use_autotools() { - [[ ${opt_yaml} == 'yes' ]] && \ - std::info \ - "Using ${FUNCNAME} is deprecated with YAML module configuration files." + std::info \ + "Using ${FUNCNAME[0]} is deprecated with YAML module configuration files." configure_with='autotools' } readonly -f pbuild::use_autotools @@ -743,9 +726,8 @@ readonly -f pbuild::use_autotools #.............................................................................. # pbuild::use_cmake() { - [[ ${opt_yaml} == 'yes' ]] && \ - std::info \ - "Using ${FUNCNAME} is deprecated with YAML module configuration files." + std::info \ + "Using ${FUNCNAME[0]} is deprecated with YAML module configuration files." configure_with='cmake' } readonly -f pbuild::use_cmake @@ -766,7 +748,7 @@ pbuild::use_cc() { "%s " "${module_name}/${module_version}:" \ "Error in setting CC:" \ "'$1' is not an executable!" - CC="$1" + export CC="$1" } readonly -f pbuild::use_cc @@ -780,9 +762,8 @@ readonly -f pbuild::use_cc declare -- compile_in_sourcetree='no' pbuild::compile_in_sourcetree() { - [[ ${opt_yaml} == 'yes' ]] && \ - std::info \ - "Using ${FUNCNAME} is deprecated with YAML module configuration files." + std::info \ + "Using ${FUNCNAME[0]} is deprecated with YAML module configuration files." compile_in_sourcetree='yes' } readonly -f pbuild::compile_in_sourcetree @@ -826,7 +807,7 @@ pbuild::configure() { if [[ -r "${SRC_DIR}/configure" ]] && \ [[ "${configure_with}" == 'auto' ]] || \ [[ "${configure_with}" == 'autotools' ]]; then - ${SRC_DIR}/configure \ + "${SRC_DIR}/configure" \ --prefix="${PREFIX}" \ "${config_args[@]}" || \ std::die 3 \ @@ -890,10 +871,8 @@ pbuild::compile() { # $@: documentation files relative to source # pbuild::install_docfiles() { - if [[ ${opt_yaml} == 'yes' ]]; then - std::info \ - "Using ${FUNCNAME} is deprecated with YAML module configuration files." - fi + std::info \ + "Using ${FUNCNAME[0]} is deprecated with YAML module configuration files." MODULE_DOCFILES+=("$@") } readonly -f pbuild::install_docfiles @@ -920,8 +899,9 @@ pbuild::install_shared_libs() { local -r pattern="${3//\//\\/}" # escape slash install_shared_libs_Linux() { - local libs=( $(ldd "${binary}" | \ - ${awk} "/ => \// && /${pattern}/ {print \$3}") ) + local -a libs=() + mapfile -t libs < <(${ldd} "${binary}" | \ + ${awk} "/ => \// && /${pattern}/ {print \$3}") if (( ${#libs[@]} > 0 )); then ${cp} -vL "${libs[@]}" "${dstdir}" || return $? fi @@ -930,8 +910,9 @@ pbuild::install_shared_libs() { install_shared_libs_Darwin() { # https://stackoverflow.com/questions/33991581/install-name-tool-to-update-a-executable-to-search-for-dylib-in-mac-os-x - local libs=( $(${otool} -L "${binary}" | \ - ${awk} "/${pattern}/ {print \$1}")) + local -a libs=() + mapfile -t libs < <(${otool} -L "${binary}" | \ + ${awk} "/${pattern}/ {print \$1}") if (( ${#libs[@]} > 0 )); then ${cp} -vL "${libs[@]}" "${dstdir}" || return $? fi @@ -943,7 +924,7 @@ pbuild::install_shared_libs() { "%s " "${module_name}/${module_version}:" \ "${binary}: does not exist or is not executable!" ${mkdir} -p "${dstdir}" - case "${OS}" in + case "${KernelName}" in Linux ) install_shared_libs_Linux ;; @@ -1028,8 +1009,9 @@ _build_module() { build_dependency() { find_build_script(){ local p=$1 - local script=$(${find} "${BUILDBLOCK_DIR}/../.." \ - -path "*/$p/build") + local script='' + script=$(${find} "${BUILDBLOCK_DIR}/../.." \ + -path "*/$p/build") std::get_abspath "${script}" } @@ -1044,7 +1026,7 @@ _build_module() { std::info "%s " \ "$m: module does not exist, trying to build it..." local args=( '' ) - set -- ${ARGS[@]} + set -- "${ARGS[@]}" while (( $# > 0 )); do case $1 in -j ) @@ -1052,23 +1034,24 @@ _build_module() { shift ;; --jobs=[0-9]* ) - args+=( $1 ) + args+=( "$1" ) ;; -v | --verbose) - args+=( $1 ) + args+=( "$1" ) ;; --with=*/* ) - args+=( $1 ) + args+=( "$1" ) ;; esac shift done - local buildscript=$(find_build_script "${m%/*}") + local buildscript='' + buildscript=$(find_build_script "${m%/*}") [[ -x "${buildscript}" ]] || \ std::die 1 \ "$m: build-block not found!" - if ! "${buildscript}" "${m#*/}" ${args[@]}; then + if ! "${buildscript}" "${m#*/}" "${args[@]}"; then std::die 1 \ "$m: oops: build failed..." fi @@ -1111,9 +1094,6 @@ _build_module() { pbuild::module_is_avail "$m" release_of_dependency || \ std::die 6 "Oops" fi - # should be set, just in case it is not... - : ${release_of_dependency:='unstable'} - # for a stable module all dependencies must be stable if [[ "${module_release}" == 'stable' ]] \ && [[ "${release_of_dependency}" != 'stable' ]]; then @@ -1131,7 +1111,7 @@ _build_module() { fi std::info "Loading module: ${m}" - eval $( "${MODULECMD}" bash load "${m}" ) + eval "$( "${modulecmd}" bash load "${m}" )" done } @@ -1140,13 +1120,11 @@ _build_module() { if [[ "${opt_yaml,,}" == 'no' ]]; then (( ${#SUPPORTED_SYSTEMS[@]} == 0 )) && return 0 for sys in "${SUPPORTED_SYSTEMS[@]}"; do - [[ ${sys,,} == ${system,,} ]] && return 0 + [[ "${sys,,}" == "${system,,}" ]] && return 0 done std::die 1 \ "%s " "${module_name}/${module_version}:" \ "Not available for ${system}." - else - : debug "Systems: $Systems" fi } @@ -1154,7 +1132,7 @@ _build_module() { check_supported_compilers() { (( ${#SUPPORTED_COMPILERS[@]} == 0 )) && return 0 for compiler in "${SUPPORTED_COMPILERS[@]}"; do - [[ ${compiler,,} == ${COMPILER,,} ]] && return 0 + [[ "${compiler,,}" == "${COMPILER,,}" ]] && return 0 done std::die 1 \ "%s " "${module_name}/${module_version}:" \ @@ -1197,8 +1175,8 @@ _build_module() { "module is in group '${GROUP}' but no HDF5 module loaded!" } - modulefile_dir="${ol_mod_root}/${GROUP}/${PMODULES_MODULEFILES_DIR}/" - PREFIX="${ol_inst_root}/${GROUP}/${module_name}/${module_version}/" + modulefile_dir="${ol_modulefiles_root}/${GROUP}/${PMODULES_MODULEFILES_DIR}/" + PREFIX="${ol_install_root}/${GROUP}/${module_name}/${module_version}/" case "${GROUP}" in Compiler ) [[ -v COMPILER_VERSION ]] || die_no_compiler @@ -1221,8 +1199,8 @@ _build_module() { [[ -v HDF5_VERSION ]] || die_no_hdf5 modulefile_dir+="${COMPILER}/${COMPILER_VERSION}/" modulefile_dir+="${MPI}/${MPI_VERSION}/" - modulefile_dir+="${HDF5}/${HDF5_VERSION}/" - PREFIX+="${HDF5}/${HDF5_VERSION}/" + modulefile_dir+="hdf5/${HDF5_VERSION}/" + PREFIX+="hdf5/${HDF5_VERSION}/" PREFIX+="${MPI}/${MPI_VERSION}/" PREFIX+="${COMPILER}/${COMPILER_VERSION}/" group_depth=6 @@ -1231,8 +1209,8 @@ _build_module() { [[ -v COMPILER_VERSION ]] || die_no_compiler [[ -v HDF5_SERIAL_VERSION ]] || die_no_hdf5 modulefile_dir+="${COMPILER}/${COMPILER_VERSION}/" - modulefile_dir+="${hdf5_serial}/${HDF5_SERIAL_VERSION}/" - PREFIX+="${hdf5_serial}/${HDF5_SERIAL_VERSION}/" + modulefile_dir+="hdf5_serial/${HDF5_SERIAL_VERSION}/" + PREFIX+="hdf5_serial/${HDF5_SERIAL_VERSION}/" PREFIX+="${COMPILER}/${COMPILER_VERSION}/" group_depth=4 ;; @@ -1270,20 +1248,11 @@ _build_module() { "${module_name}/${module_version}:" \ "Installing documentation to ${docdir}" ${install} -m 0755 -d "${docdir}" - ${install} -m0644 "${BUILD_SCRIPT}" "${docdir}" - "${MODULECMD}" bash list -t 2>&1 1>/dev/null | \ + ${install} -m 0644 "${BUILD_SCRIPT}" "${docdir}" + "${modulecmd}" bash list -t 2>&1 1>/dev/null | \ ${grep} -v "Currently Loaded" > \ "${docdir}/dependencies" || : - # loop over version specific functions. In these function - # more MODULE_DOCFILES can be defined. - # :FIXME: maybe we find a better solution. - for f in ${VERSIONS[@]/#/pbuild::install_docfiles_}; do - if typeset -F "$f" 2>/dev/null; then - $f - break - fi - done (( ${#MODULE_DOCFILES[@]} == 0 )) && return 0 ${install} -m0644 \ "${MODULE_DOCFILES[@]/#/${SRC_DIR}/}" \ @@ -1307,28 +1276,29 @@ _build_module() { if [[ ! $dep == */* ]]; then # no version given: derive the version # from the currently loaded module - dep=$( "${MODULECMD}" bash list -t 2>&1 1>/dev/null \ + dep=$( "${modulecmd}" bash list -t 2>&1 1>/dev/null \ | grep "^${dep}/" ) fi echo "${dep}" >> "${fname}" done } patch_elf_exe_and_libs(){ - local -- libdir="${OverlayInfo[${ol_name}:inst_root]}/lib64" + local -- libdir="${OverlayInfo[${ol_name}:install_root]}/lib64" [[ -d "${libdir}" ]] || return 0 - local -a bin_objects=( $(std::find_executables '.' ) ) + local -a bin_objects=() + mapfile -t bin_objects < <(std::find_executables '.') local -- fname='' + local -- rpath='' + local -i depth=0 for fname in "${bin_objects[@]}"; do - local -i depth=$(std::get_dir_depth "${fname}") - (( depth+=group_depth+3 )) - local -- rpath='$ORIGIN/'$(printf "../%.0s" $(${seq} 1 ${depth}))lib64 + (( depth=$(std::get_dir_depth "${fname}") + group_depth + 3 )) + rpath='$ORIGIN/'$(printf "../%.0s" $(${seq} 1 ${depth}))lib64 ${patchelf} --force-rpath --set-rpath "${rpath}" "${fname}" done - local -a bin_objects=( $(std::find_shared_objects '.' ) ) + mapfile -t bin_objects < <(std::find_shared_objects '.') for fname in "${bin_objects[@]}"; do - local -i depth=$(std::get_dir_depth "${fname}") - (( depth+=group_depth+3 )) - local -- rpath='$ORIGIN/'$(printf "../%.0s" $(${seq} 1 ${depth}))lib64 + (( depth=$(std::get_dir_depth "${fname}") + group_depth + 3 )) + rpath='$ORIGIN/'$(printf "../%.0s" $(${seq} 1 ${depth}))lib64 ${patchelf} --force-rpath --set-rpath "${rpath}" "${fname}" done } @@ -1340,7 +1310,7 @@ _build_module() { std::info \ "%s " \ "${module_name}/${module_version}:" \ - "running post-installation for ${OS} ..." + "running post-installation for ${KernelName} ..." cd "${PREFIX}" [[ -d "lib" ]] && [[ ! -d "lib64" ]] && ln -s lib lib64 patch_elf_exe_and_libs @@ -1350,7 +1320,7 @@ _build_module() { #.............................................................. # post-install cd "${BUILD_DIR}" - [[ "${OS}" == "Linux" ]] && post_install_linux + [[ "${KernelName}" == "Linux" ]] && post_install_linux install_doc if (( ${#runtime_dependencies[@]} > 0 )); then write_runtime_dependencies \ @@ -1374,7 +1344,7 @@ _build_module() { } # post_install #...................................................................... - # Install modulefile in ${ol_mod_root}/${GROUP}/modulefiles/... + # Install modulefile in ${ol_modulefiles_root}/${GROUP}/modulefiles/... # The modulefiles in the build-block can be # versioned like # modulefile-10.2.0 @@ -1413,8 +1383,7 @@ _build_module() { } [[ "${is_subpkg}" == 'yes' ]] && return 0 local src='' - find_modulefile src - if (( $? != 0 )); then + if ! find_modulefile src; then std::info \ "%s " \ "${module_name}/${module_version}:" \ @@ -1434,8 +1403,8 @@ _build_module() { local ol='' for ol in "${Overlays[@]}"; do [[ "${ol}" == "${ol_name}" ]] && continue - local mod_root="${OverlayInfo[${ol}:mod_root]}" - local dir="${modulefile_dir/${ol_mod_root}/${mod_root}}" + local modulefiles_root="${OverlayInfo[${ol}:modulefiles_root]}" + local dir="${modulefile_dir/${ol_modulefiles_root}/${modulefiles_root}}" local fname="${dir}/${module_version}" if [[ -e "${fname}" ]]; then std::info "%s "\ @@ -1459,12 +1428,9 @@ _build_module() { local -r legacy_config_file="${modulefile_dir}/.release-${module_version}" local -- status_legay_config_file='unchanged' - local -r yaml_config_file="${modulefile_dir}/.config-${module_version}" - local -- status_yaml_config_file='unchanged' - + local -- relstage_legacy='' if [[ -r "${legacy_config_file}" ]]; then - local relstage_legacy - read relstage_legacy < "${legacy_config_file}" + read -r relstage_legacy < "${legacy_config_file}" if [[ "${relstage_legacy}" != "${module_release}" ]]; then status_legay_config_file='changed' fi @@ -1476,8 +1442,10 @@ _build_module() { echo "${module_release}" > "${legacy_config_file}" fi + local -r yaml_config_file="${modulefile_dir}/.config-${module_version}" + local -- status_yaml_config_file='unchanged' if [[ -r "${yaml_config_file}" ]]; then - while read key value; do + while read -r key value; do local -n ref="${key:0:-1}" ref="${value}" done < "${yaml_config_file}" @@ -1583,26 +1551,26 @@ _build_module() { return 0 fi local targets=() - targets+=( ${VERSIONS[@]/#/pbuild::pre_${target}_${system}_} ) - targets+=( pbuild::pre_${target}_${system} ) - targets+=( ${VERSIONS[@]/#/pbuild::pre_${target}_${OS}_} ) - targets+=( pbuild::pre_${target}_${OS} ) - targets+=( ${VERSIONS[@]/#/pbuild::pre_${target}_} ) - targets+=( pbuild::pre_${target} ) + targets+=( "${VERSIONS[@]/#/pbuild::pre_${target}_${system}_}" ) + targets+=( "pbuild::pre_${target}_${system}" ) + targets+=( "${VERSIONS[@]/#/pbuild::pre_${target}_${KernelName}_}" ) + targets+=( "pbuild::pre_${target}_${KernelName}" ) + targets+=( "${VERSIONS[@]/#/pbuild::pre_${target}_}" ) + targets+=( "pbuild::pre_${target}" ) - targets+=( ${VERSIONS[@]/#/pbuild::${target}_${system}_} ) - targets+=( pbuild::${target}_${system} ) - targets+=( ${VERSIONS[@]/#/pbuild::${target}_${OS}_} ) - targets+=( pbuild::${target}_${OS} ) - targets+=( ${VERSIONS[@]/#/pbuild::${target}_} ) - targets+=( pbuild::${target} ) + targets+=( "${VERSIONS[@]/#/pbuild::${target}_${system}_}" ) + targets+=( "pbuild::${target}_${system}" ) + targets+=( "${VERSIONS[@]/#/pbuild::${target}_${KernelName}_}" ) + targets+=( "pbuild::${target}_${KernelName}" ) + targets+=( "${VERSIONS[@]/#/pbuild::${target}_}" ) + targets+=( "pbuild::${target}" ) - targets+=( ${VERSIONS[@]/#/pbuild::post_${target}_${system}_} ) - targets+=( pbuild::post_${target}_${system} ) - targets+=( ${VERSIONS[@]/#/pbuild::post_${target}_${OS}_} ) - targets+=( pbuild::post_${target}_${OS} ) - targets+=( ${VERSIONS[@]/#/pbuild::post_${target}_} ) - targets+=( pbuild::post_${target} ) + targets+=( "${VERSIONS[@]/#/pbuild::post_${target}_${system}_}" ) + targets+=( "pbuild::post_${target}_${system}" ) + targets+=( "${VERSIONS[@]/#/pbuild::post_${target}_${KernelName}_}" ) + targets+=( "pbuild::post_${target}_${KernelName}" ) + targets+=( "${VERSIONS[@]/#/pbuild::post_${target}_}" ) + targets+=( "pbuild::post_${target}" ) for t in "${targets[@]}"; do # We cd into the dir before calling the function - @@ -1661,7 +1629,7 @@ _build_module() { "%s " \ "${module_name}/${module_version}:" \ "removing all files in '${PREFIX}' ..." - [[ "${dry_run}" == 'no' ]] && ${rm} -rf ${PREFIX} + [[ "${dry_run}" == 'no' ]] && ${rm} -rf "${PREFIX}" fi if [[ -e "${modulefile_name}" ]]; then std::info \ diff --git a/Pmodules/libpmodules.bash.in b/Pmodules/libpmodules.bash.in index 6370644..7a77eda 100644 --- a/Pmodules/libpmodules.bash.in +++ b/Pmodules/libpmodules.bash.in @@ -1,16 +1,11 @@ #!/bin/bash declare PMODULES_MODULEFILES_DIR='modulefiles' -declare PMODULES_CONFIG_DIR='config' declare PMODULES_VERSION='@PMODULES_VERSION@' declare -A GroupDepths=() -declare -A Subcommands=() -declare -A Options=() -declare -A Help=() declare -a Overlays=() declare -A OverlayInfo -declare -a UsedOverlays declare -A Dir2OverlayMap declare -r ol_normal='n' @@ -18,21 +13,6 @@ declare -r ol_hiding='h' declare -r ol_replacing='r' -# initialize help text of 'module --version' -Help['version']=" -Pmodules @PMODULES_VERSION@ -using Tcl Environment Modules -VERSION = @MODULES_VERSION@ -" - -# -# display help text for command given in $1 -# -print_help() { - echo -e "${Help[$1]}" 1>&2 - std::die 1 -} - # # compute depth of modulefile directory. # @@ -50,10 +30,10 @@ compute_group_depth () { local group=${group##*/} result=$(${find} "${dir}" -depth \( -type f -o -type l \) \ -printf "%d" -quit 2>/dev/null) - (( result-=2 )) + (( result-=2 )) || : # if a group doesn't contain a modulefile, depth is negativ # :FIXME: better solution? - (( result < 0 )) && (( result = 0 )) + (( result < 0 )) && (( result = 0 )) || : } # @@ -63,173 +43,236 @@ compute_group_depth () { # $@: overlay names # scan_groups () { - local ol + local ol local depth for ol in "$@"; do - local mod_root="${OverlayInfo[${ol}:mod_root]}" + local modulefiles_root="${OverlayInfo[${ol}:modulefiles_root]}" local dir - for dir in ${mod_root}/*/${PMODULES_MODULEFILES_DIR}; do + for dir in "${modulefiles_root}"/*/"${PMODULES_MODULEFILES_DIR}"; do local group="${dir%/*}" group="${group##*/}" if [[ ! -v GroupDepths[${group}] ]]; then compute_group_depth depth "${dir}" GroupDepths[$group]=${depth} fi - Dir2OverlayMap[${dir%/${PMODULES_MODULEFILES_DIR}*}]="${ol}" + Dir2OverlayMap[${dir%/"${PMODULES_MODULEFILES_DIR}"*}]="${ol}" done done } -pm::read_config(){ - local -a config_files=() +declare -A DefaultPmodulesConfig=( + ['defaultgroups']='Tools:Programming' + ['default_groups']='Tools:Programming' + ['defaultreleasestages']='stable' + ['default_reltages']='stable' + ['tmpdir']="/opt/psi/var/tmp/${USER}" + ['tmp_dir']="/opt/psi/var/tmp/${USER}" + ['distfilesdir']='/opt/psi/var/distfiles' + ['distfiles_dir']='/opt/psi/var/distfiles' + ['download_dir']='/opt/psi/var/distfiles' + ['overlays']='' +) + +declare -A OverlayConfigKeys=( + ['install_root']='/opt/psi' + ['modulefiles_root']='' + ['excludes']='' + ['type']='n' +) + +pm::get_value(){ + local -- yaml_input="$1" + local -n val="$2" + local -- key="$3" + local -- expected_type="$4" - _get_config_files(){ - # - # return array with overlay configuration files - # - # Args: - # $1 [upvar] result - local -n fnames="$1" + local -- type='' + type=$( ${yq} -e ".${key} | type" 2>/dev/null <<<"${yaml_input}") + if [[ "${type}" != "${expected_type}" ]]; then + std::die 3 "%s" \ + "Value of '${key}' must be of type '${expected_type:2}', but is '${type:2}'!" + fi + val=$( ${yq} -e ".${key}" \ + 2>/dev/null <<<"${yaml_input}" ) || val='' +} +pm::get_seq(){ + local -- yaml_input="$1" + local -n val="$2" + local -- key="$3" - # user defined via environment variable - if [[ -v PMODULES_OVERLAYS_DEF ]]; then - test -r "${PMODULES_OVERLAYS_DEF}" || \ - std::die 3 \ - "%s -- %s" \ - "overlay definition file is not readable" \ - "$_" - fnames+=("${PMODULES_OVERLAYS_DEF}") - fi - - # user defined - if [[ -r "${HOME}/.Pmodules/Pmodules.yaml" ]]; then - fnames+=("${HOME}/.Pmodules/Pmodules.yaml") - fi - - # system config file - test -r "${PMODULES_HOME%%/Tools*}/config/Pmodules.yaml" || \ - std::die 3 \ - "%s %s -- %s" \ - "base overlay definition file" \ - "does not exist or is not readable" \ - "$_" - - fnames+=("${PMODULES_HOME%%/Tools*}/config/Pmodules.yaml") - } - - _get_ol_names(){ - # - # get the names of all overlays - # - local -n fnames="$1" - ${yq} -Ne eval-all '. as $item ireduce ({}; . *+ $item) |.Overlays|keys()' \ - "${fnames[@]}" | awk '{print $2}' - } - - _get_install_root(){ - local -n fnames="$1" - local -- _ol="$2" - local -- _tmp - _tmp=$(${yq} -Ne eval-all ". as \$item ireduce ({}; . *+ \$item) |.Overlays.${_ol}.install_root" \ - "${fnames[@]}" 2>/dev/null) || return 1 - echo "${_tmp}" | envsubst - } - - _get_modulefiles_root(){ - local -n fnames="$1" - local -- _ol="$2" - local -- _tmp - _tmp=$(${yq} -Ne eval-all ". as \$item ireduce ({}; . *+ \$item) |.Overlays.${_ol}.modulefiles_root" \ - "${fnames[@]}" 2>/dev/null) || return 1 - echo "${_tmp}" | envsubst - } - - _get_excludes(){ - local -n fnames="$1" - local -- _ol="$2" - ${yq} -Ne eval-all ". as \$item ireduce ({}; . *+ \$item) |.Overlays.${_ol}.excludes[]" \ - "${fnames[@]}" 2>/dev/null - } - - _get_type(){ - local -n fnames="$1" - local -- _ol="$2" - ${yq} -Ne eval-all ". as \$item ireduce ({}; . *+ \$item) |.Overlays.${_ol}.type" \ - "${fnames[@]}" 2>/dev/null - } - - _join_array() { - local IFS="$1" - shift - echo -n "$*" - } - - _get_config_files config_files - - eval $(std::parse_yaml "${config_files[-1]}" 'cfg_') - [[ -v cfg_DefaultGroups ]] && DefaultGroups="${cfg_DefaultGroups}" - [[ -v cfg_DefaultReleaseStages ]] && DefaultReleaseStages="${cfg_DefaultReleaseStages}" - [[ -v cfg_ReleaseStages ]] && ReleaseStages="${cfg_ReleaseStages}" - [[ -v cfg_SysCollectionsDir ]] && SysCollectionsDir="${cfg_SysCollectionsDir}" - unset ${!cfg_*} - - # - # loop over all overlays and save config in OverlayInfo and Dir2OverlayMap - # - # - at least install_root must be specified - # - modulefiles_root default so install_root - # - the type defaults to ${ol_normal} - # - Overlays=( $(_get_ol_names config_files) ) - local ol='' - local install_root='' - local modulefiles_root='' - local type='' - local excludes=() - for ol in "${Overlays[@]}"; do - # install_root - install_root=$(_get_install_root config_files "${ol}") || \ - std::die 3 \ - "Install_root missing for overlay -- ${ol}" - install_root=$(envsubst <<< "${install_root}") - [[ -d ${install_root} ]] || \ - std::die 3 \ - "Invalid installation root directory for overlay '${ol}' -- ${install_root}" - OverlayInfo[${ol}:inst_root]="${install_root}" - - # modulefiles_root - modulefiles_root=$(_get_modulefiles_root config_files "${ol}") || \ - modulefiles_root=${install_root} - [[ -d ${modulefiles_root} ]] || \ - std::die 3 \ - "Invalid modulefiles root directory for overlay '${ol}' -- ${modulefiles_root}" - OverlayInfo[${ol}:mod_root]="${modulefiles_root}" - Dir2OverlayMap[${modulefiles_root}]="${ol}" - - # type - type=$(_get_type config_files "${ol}") || \ - type=${ol_normal} - case ${type} in - ${ol_normal} | ${ol_replacing} | ${ol_hiding} ) - : - ;; - * ) - std::die 3 "Invalid type for overlay '${ol}' -- ${type}" - ;; - esac - OverlayInfo[${ol}:type]="${type}" - - # excludes - excludes=$(_get_excludes config_files "${ol}") || \ - excludes=() - OverlayInfo[${ol}:excludes]=$(_join_array ':' "${excludes[@]}") - - # mark as unused - OverlayInfo[${ol}:used]='no' - done + local -- type='' + type=$( ${yq} ".${key} | type" 2>/dev/null <<<"${yaml_input}") + if [[ "${type:2}" == 'null' ]]; then + val='' + return 0 + fi + if [[ "${type}" != '!!seq' ]]; then + std::die 3 "%s" \ + "Value of '${key}' must be of type 'seq', but is of type '${type:2}'!" + fi + val=$( ${yq} -e ".${key}[]" \ + 2>/dev/null <<<"${yaml_input}" ) || val='' } +pm::read_config(){ + : " + Read Pmodules configuration files '${PMODULES_HOME}/config/Pmodules.yaml' + and '${HOME}/.Pmodules/Pmodules.yaml'. + + Args: + none + " + get_config_of_overlay(){ + : " + Get configuration of an overlay. + + Args: + $1 YAML config + $2 name of overlay + " + local -r yaml_input="$1" + local -r ol_name="$2" + local -- key='' + local -a keys=() + local -- value='' + # init overlay with defaults + for key in "${!OverlayConfigKeys[@]}"; do + OverlayInfo[${ol_name}:${key}]="${OverlayConfigKeys[${key}]}" + done + # get keys in YAML input + readarray -t keys < <( ${yq} -e ".|keys|.[]" <<<"${yaml_input}" 2>/dev/null ) || \ + std::die 3 "Oops: retrieving keys from:\n${yaml_input}" + for key in "${keys[@]}"; do + [[ -v OverlayConfigKeys[${key,,}] ]] || \ + std::die 3 "%s -- %s\n%s" \ + "Invalid key in configuration" \ + "${key}" "${yaml_input}" + case ${key,,} in + install_root ) + pm::get_value "${yaml_input}" value "${key}" '!!str' + OverlayInfo[${ol_name}:install_root]=$(${envsubst} <<< "${value}") + [[ -d ${OverlayInfo[${ol_name}:install_root]} ]] || \ + std::die 3 \ + "Invalid installation root directory for overlay '${ol_name}' -- ${value}" + ;; + modulefiles_root ) + pm::get_value "${yaml_input}" value "${key}" '!!str' + OverlayInfo[${ol_name}:modulefiles_root]=$(${envsubst} <<< "${value}") + [[ -d ${OverlayInfo[${ol_name}:modulefiles_root]} ]] || \ + std::die 3 \ + "Invalid modulefiles root directory for overlay '${ol_name}' -- ${value}" + ;; + type ) + pm::get_value "${yaml_input}" value "${key}" '!!str' + case ${value} in + "${ol_normal}" | "${ol_replacing}" | "${ol_hiding}" ) + : + ;; + * ) + std::die 3 "Invalid type for overlay '${ol_name}' -- ${type}" + ;; + esac + OverlayInfo[${ol_name}:type]="${value}" + ;; + excludes ) + pm::get_seq "${yaml_input}" value "${key}" '!!seq' + local -a tmp_array=() + read -r -a tmp_array <<<"${value}" + local excludes='' + printf -v excludes "%s:" "${tmp_array[@]}" + OverlayInfo[${ol_name}:excludes]="${excludes%:}" + ;; + esac + done + if [[ -z "${OverlayInfo[${ol_name}:modulefiles_root]}" ]]; then + OverlayInfo[${ol_name}:modulefiles_root]=${OverlayInfo[${ol_name}:install_root]} + fi + local modulefiles_root=${OverlayInfo[${ol_name}:modulefiles_root]} + Dir2OverlayMap[${modulefiles_root}]="${ol_name}" + } + + get_config_of_overlays(){ + : " + Get configuration of overlays in YAML input. + + Args: + $1 YAML input + " + local -r yaml_input="$1" + local -- ol_name='' + readarray -t Overlays < <( ${yq} -e '.|keys|.[]' <<<"${yaml_input}" ) + for ol_name in "${Overlays[@]}"; do + local yaml_tmp + pm::get_value "${yaml_input}" yaml_tmp "${ol_name}" '!!map' + get_config_of_overlay "${yaml_tmp}" "${ol_name}" + done + } + + get_config(){ + : " + Get Pmodules configuration. + + Args: + $1 Pmodules configuration file + " + local -r config_file="$1" + local -- yaml_input='' + yaml_input=$( ${yq} -Ne e "." "${config_file}" 2>/dev/null ) || \ + std::die 3 "Cannot read configuration file '${config_file}'!" + + local -a keys=() + readarray -t keys < <( ${yq} -e ".|keys().[]" <<<"${yaml_input}" 2>/dev/null ) || \ + std::die 3 "Oops: retrieving keys from:\n${yaml_input}" + + for key in "${keys[@]}"; do + [[ -v DefaultPmodulesConfig[${key,,}] ]] || \ + std::die 3 "%s -- %s\n%s" \ + "Invalid key in configuration" \ + "${key}" "${yaml_input}" + case ${key,,} in + defaultgroups | default_groups ) + pm::get_value "${yaml_input}" DefaultGroups "${key}" '!!str' + ;; + defaultreleasestages | default_reltages ) + pm::get_value "${yaml_input}" DefaultReleaseStages "${key}" '!!str' + ;; + tmpdir | tmp_dir ) + pm::get_value "${yaml_input}" TmpDir "${key}" '!!str' + ;; + distfilesdir | download_dir ) + pm::get_value "${yaml_input}" DistfilesDir "${key}" '!!str' + ;; + overlays ) + local tmp_str='' + pm::get_value "${yaml_input}" tmp_str "${key}" '!!map' + get_config_of_overlays "${tmp_str}" + ;; + esac + done + } + + # system config file + local -r sys_config_file="${PMODULES_HOME%%/Tools*}/config/Pmodules.yaml" + test -r "${sys_config_file}" || \ + std::die 3 \ + "%s %s -- %s" \ + "base overlay definition file" \ + "does not exist or is not readable" \ + "$_" + DefaultGroups="${DefaultPmodulesConfig['default_groups']}" + DefaultReleaseStages="${DefaultPmodulesConfig['default_reltages']}" + TmpDir="${DefaultPmodulesConfig['tmp_dir']}" + DistfilesDir="${DefaultPmodulesConfig['download_dir']}" + + get_config "${sys_config_file}" + + local -r usr_config_file="${HOME}/.Pmodules/Pmodules.yaml" + if [[ -r "${usr_config_file}" ]]; then + get_config "${usr_config_file}" + fi +} + # Local Variables: # mode: sh # sh-basic-offset: 8 diff --git a/Pmodules/libstd.bash b/Pmodules/libstd.bash index 65c2a0f..36a5474 100644 --- a/Pmodules/libstd.bash +++ b/Pmodules/libstd.bash @@ -27,7 +27,7 @@ std::debug() { std::die() { local -ri ec=$1 shift - if [[ -n $@ ]]; then + if (( ${#@} > 0 )); then local -r fmt=$1 shift std::log 2 "$fmt" "$@" @@ -39,16 +39,51 @@ std::def_cmd(){ which "$1" 2>/dev/null || std::die 255 "'$1' not found!" } -std::def_cmds(){ - local path="$1" - shift - for cmd in "$@"; do - eval declare -gr ${cmd}=$(PATH="${path}" /usr/bin/which $cmd 2>/dev/null) - if [[ -z "${!cmd}" ]]; then - std::die 255 "${cmd} not found" - fi - done -} +awk=$(std::def_cmd 'awk'); declare -r awk +base64=$(std::def_cmd 'base64'); declare -r base64 +bash=$(std::def_cmd 'bash'); declare -r bash +cat=$(std::def_cmd 'cat'); declare -r cat +cp=$(std::def_cmd 'cp'); declare -r cp +curl=$(std::def_cmd 'curl'); declare -r curl +envsubst=$(std::def_cmd 'envsubst'); declare -r envsubst +dirname=$(std::def_cmd 'dirname'); declare -r dirname +file=$(std::def_cmd 'file'); declare -r file +find=$(std::def_cmd 'find'); declare -r find +getopt=$(std::def_cmd 'getopt'); declare -r getopt +grep=$(std::def_cmd 'grep'); declare -r grep +install=$(std::def_cmd 'install'); declare -r install +logger=$(std::def_cmd 'logger'); declare -r logger +make=$(std::def_cmd 'make'); declare -r make +mkdir=$(std::def_cmd 'mkdir'); declare -r mkdir +mktemp=$(std::def_cmd 'mktemp'); declare -r mktemp +modulecmd=$(std::def_cmd 'modulecmd'); declare -- modulecmd +patch=$(std::def_cmd 'patch'); declare -r patch +pwd=$(std::def_cmd 'pwd'); declare -r pwd +rm=$(std::def_cmd 'rm'); declare -r rm +rmdir=$(std::def_cmd 'rmdir'); declare -r rmdir +sed=$(std::def_cmd 'sed'); declare -r sed +seq=$(std::def_cmd 'seq'); declare -r seq +sevenz=$(std::def_cmd 'sevenz'); declare -r sevenz +sort=$(std::def_cmd 'sort'); declare -r sort +tar=$(std::def_cmd 'tar'); declare -r tar +tee=$(std::def_cmd 'tee'); declare -r tee +tput=$(std::def_cmd 'tput'); declare -r tput +uname=$(std::def_cmd 'uname'); declare -r uname +yq=$(std::def_cmd 'yq'); declare -r yq + +KernelName=$(${uname} -s); declare -r KernelName +if [[ ${KernelName} == 'Darwin' ]]; then + PATH+=':/opt/local/bin' + otool=$(std::def_cmd 'otool'); declare -r otool + shasum=$(std::def_cmd 'shasum');declare -r shasum + sysctl=$(std::def_cmd 'sysctl');declare -r sysctl + declare -r sha256sum="${shasum -a 256}" +else + ldd=$(std::def_cmd 'ldd'); declare -r ldd + patchelf=$(std::def_cmd 'patchelf'); declare -r patchelf + sha256sum=$(std::def_cmd 'sha256sum'); + declare -r sha256sum +fi # # get answer to yes/no question @@ -58,7 +93,7 @@ std::def_cmds(){ std::get_YN_answer() { local -r prompt="$1" local ans - read -p "${prompt}" ans + read -r -p "${prompt}" ans case ${ans} in y|Y ) return 0;; @@ -71,14 +106,16 @@ std::get_YN_answer() { # return normalized abolute pathname # $1: filename std::get_abspath() { - local -r fname=$1 + local -r fname="$1" + local -- abspath='' #[[ -r "${fname}" ]] || return 1 if [[ -d ${fname} ]]; then - echo $(cd "${fname}" && pwd) + abspath=$(cd "${fname}" && pwd -L) else local -r dname=$(dirname "${fname}") - echo $(cd "${dname}" && pwd)/$(basename "${fname}") + abspath=$(cd "${dname}" && pwd -L)/$(basename "${fname}") fi + echo "${abspath}" } std::append_path () { @@ -117,135 +154,20 @@ std::prepend_path () { } std::remove_path() { - local -nr P="$1" + local -nr path="$1" shift 1 - local -ar dirs="$@" + local -ar remove_dirs=("$@") local new_path='' - local -r _P=( ${P//:/ } ) + local -a _path=() + IFS=':' read -r -a _path <<<"${path}" local dir='' - for dir in "${dirs[@]}"; do + for dir in "${remove_dirs[@]}"; do # loop over all entries in path - for entry in "${_P[@]}"; do + for entry in "${_path[@]}"; do [[ "${entry}" != "${dir}" ]] && new_path+=":${entry}" done done - P="${new_path:1}" # remove leading ':' -} - -# -# 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|:$||')" -} - -# -# Functions to split a path into its components. -# -# Args: -# $1 upvar -# $2 absolute or relative path (depends on the function) -# $3 opt upvar: number of components -# -# Notes: -# std::split_path() -# if the path is absolute, the first element of the returned array is empty. -# -# std::split_abspath() -# the path must begin with a slash, otherwise std::die() is called with -# an internal error message. -# -# std::split_relpath() -# analog to std::split_abspath() with a relative path. -# -std::split_path() { - local -n parts="$1" - local -r path="$2" - - IFS='/' - local std__split_path_result=( ${std__split_path_tmp} ) - unset IFS - parts="${std__split_path_result[@]}" - if (( $# >= 3 )); then - # return number of parts - local -n num="$3" - num="${#std__split_path_result[@]}" - fi -} - -std::split_abspath() { - local -n parts="$1" - local -r path="$2" - if [[ "${path:0:1}" == '/' ]]; then - local -r std__split_path_tmp="${path:1}" - else - std::die 255 "Oops: Internal error in '${FUNCNAME[0]}' called by '${FUNCNAME[1]}' }" - fi - - IFS='/' - local std__split_path_result=( ${std__split_path_tmp} ) - unset IFS - parts="${std__split_path_result[@]}" - if (( $# >= 3 )); then - # return number of parts - local -n num="$3" - num="${#std__split_path_result[@]}" - fi -} - -std::split_relpath() { - local -n parts="$1" - local -r path="$2" - if [[ "${path:0:1}" == '/' ]]; then - std::die 255 "Oops: Internal error in '${FUNCNAME[0]}' called by '${FUNCNAME[1]}' }" - else - local -r std__split_path_tmp="${path}" - fi - - IFS='/' - local std__split_path_result=( ${std__split_path_tmp} ) - unset IFS - parts="${std__split_path_result[@]}" - if (( $# >= 3 )); then - # return number of parts - local -n num="$3" - num="${#std__split_path_result[@]}" - fi -} - -std::read_versions() { - local -r fname="$1" - local varname='' - while read _name _version; do - [[ -z ${_name} ]] && continue - [[ -z ${_version} ]] && continue - [[ "${_name:0:1}" == '#' ]] && continue - var_name=$(echo ${_name} | tr [:lower:] [:upper:])_VERSION - # don't set version, if already set - if [[ -z ${!var_name} ]]; then - eval ${var_name}="${_version}" - fi - done < "${fname}" + path="${new_path:1}" # remove leading ':' } std.get_os_release_linux() { @@ -288,7 +210,8 @@ std::get_os_release() { } std::get_type() { - local -a signature=( $(typeset -p "$1") ) + local -a signature=() + read -r -a signature <(typeset -p "$1") case ${signature[1]} in -Ai* ) echo 'int dict' @@ -314,30 +237,6 @@ std::get_type() { esac } -std::parse_yaml() { - # - # parse a YAML file - # See: https://gist.github.com/pkuczynski/8665367 - # - local -r fname="$1" - local -r prefix="$2" - local s='[[:space:]]*' w='[a-zA-Z0-9_]*' fs=$(echo @|tr @ '\034') - sed -ne "s|^\($s\)\($w\)$s:$s\"\(.*\)\"$s\$|\1$fs\2$fs\3|p" \ - -e "s|^\($s\)\($w\)$s:$s\(.*\)$s\$|\1$fs\2$fs\3|p" "${fname}" | - awk -F$fs '{ - indent = length($1)/2; - vname[indent] = $2; - for (i in vname) { - if (i > indent) {delete vname[i]} - } - if (length($3) > 0) { - vn=""; - for (i=0; i&2; exit 3; } - -############################################################################## -# -# check availability of used commands and set environment variables -# cmd=$(which cmd) -# in the following we use these environment variable to call binaries. -# -declare -r MODULECMD="${PMODULES_HOME}/bin/modulecmd" -[[ -x ${MODULECMD} ]] || \ - std::die 1 "Oops: modulecmd binary not available!" - -std::def_cmds "${mydir}/../libexec" \ - 'patchelf' 'sevenz' 'yq' - -std::def_cmds '/usr/bin:/bin:/usr/sbin:/sbin' \ - 'awk' 'base64' 'cat' 'cp' 'envsubst' 'file' 'find' 'getopt' 'grep' \ - 'install' 'logger' 'make' 'mkdir' 'mktemp' 'patch' 'pwd' \ - 'rm' 'rmdir' 'seq' 'sort' 'tar' 'tee' 'uname' - -declare -r OS="$(${uname} -s)" -if [[ ${OS} == 'Darwin' ]]; then - std::def_cmds '/usr/bin:/bin:/usr/sbin:/sbin:/opt/local/bin' \ - 'curl' 'otool' 'shasum' 'sysctl' - declare -r sha256sum="${shasum -a 256}" -else - std::def_cmds '/usr/bin:/bin:/usr/sbin:/sbin' \ - 'ldd' 'curl' 'sha256sum' -fi - -# for the time being, we still set PATH. Just in case we forgot a binary -PATH="${PMODULES_HOME}/bin:/usr/bin:/bin:/usr/sbin:/sbin" - -############################################################################## -source "${mydir}/../lib/libpbuild.bash" || \ +source "${prefix}/lib/libpmodules.bash" || \ std::die 3 "Oops: cannot source library -- '$_'" -source "${mydir}/../lib/libpmodules.bash" || \ +source "${prefix}/lib/libpbuild.bash" || \ std::die 3 "Oops: cannot source library -- '$_'" - -############################################################################## -set -o nounset -shopt -s nocaseglob -shopt -s extglob -shopt -s nullglob +unset mydir +unset prefix ############################################################################## # @@ -143,13 +117,6 @@ MISCELLANEOUS OPTIONS: --tmpdir Directory used for building a module. ---legacy - Use legacy configuration files. - ---overlay - Install in this overlay. Defaults to the base overlay. This - option can only be used with legacy configuration file. - DOCUMENTATION: Full documentation is available at http://pmodules.gitpages.psi.ch @@ -166,7 +133,7 @@ DOCUMENTATION: # last default # save arguments, required for building dependencies -declare -r ARGS="$@" +declare -ra ARGS=( "$@" ) # versions to be build, '.*' or none means all declare versions_to_build='' @@ -178,14 +145,12 @@ declare opt_force_rebuild='no' declare -i opt_jobs=0 declare opt_update_modulefiles='no' declare opt_system='' -declare opt_overlay='' declare opt_verbose='no' # array collecting all modules specified on the command line via '--with=module' -declare -a opt_with_modules=() # :FIXME: legacy build should also use the dict +declare -a opt_with_modules=() declare -A opt_with_dict=() declare -- opt_config_file='' declare -- opt_debug='no' -declare -- opt_yaml='yes' declare -- opt_check_mode='no' declare -- opt_variant='' declare -- opt_clean_install='no' @@ -193,7 +158,6 @@ declare -- opt_parent_prefix='' declare -- BUILD_SCRIPT='' declare -- yaml_config_file='' -declare -a legacy_config_files=() declare -- module_name='' declare -- module_type='module' declare -- echo=':' @@ -272,23 +236,9 @@ parse_args() { shift fi ;; - --overlay | --overlay=* ) - if [[ $1 == *=* ]]; then - opt_overlay="${1#--*=}" - else - opt_overlay="$2" - shift - fi - ;; - --yaml ) - opt_yaml='yes' - ;; --check-mode ) opt_check_mode='yes' ;; - --legacy ) - opt_yaml='no' - ;; --use-flags | --use-flags=* ) if [[ $1 == *=* ]]; then USE_FLAGS=":${1#--*=}:" @@ -334,9 +284,9 @@ parse_args() { ;; --parent-prefix | --parent-prefix=* ) if [[ $1 == *=* ]]; then - opt_parent_prefix=( "${1#--*=}" ) + opt_parent_prefix="${1#--*=}" else - opt_parent_prefix=( "$2" ) + opt_parent_prefix="$2" shift fi module_type='sub_package' @@ -375,152 +325,13 @@ parse_args() { opt_system="${opt_system:-$(std::get_os_release)}" # set config file - if [[ ${opt_yaml,,} == 'no' ]]; then - # look for legacy config files - if [[ -n ${opt_config_file} ]]; then - legacy_config_files=( "${opt_config_file}" ) - else - shopt -q nullglob || : - local -i nullglob_set=$? - - legacy_config_files=( "${BUILDBLOCK_DIR}"/*/variants.${opt_system} ) - legacy_config_files+=( "${BUILDBLOCK_DIR}"/*/variants."$(uname -s)" ) - legacy_config_files+=( "${BUILDBLOCK_DIR}"/*/variants ) - (( nullglob_set == 1 )) && shopt -u nullglob - fi - (( ${#legacy_config_files[@]} == 0 )) && \ - std::die 1 "No legacy configuration file found!" - std::info "Using legacy variants files." - opt_overlay="${opt_overlay:-'base'}" - else - yaml_config_file="${opt_config_file:-${BUILDBLOCK_DIR}/files/config.yaml}" - [[ -r "${yaml_config_file}" ]] || \ - std::die 2 \ - "%s -- %s" \ - "YAML config file doesn't exist or is not readable" \ - "${yaml_config_file}" - [[ "${opt_overlay}" == 'yes' ]] && \ - std::die 2 \ - "opt '--overlay' can only be used together with legacy config files!" - std::info "Using YAML configuration file - ${yaml_config_file}" - fi -} - -# -# bash brace expansion of given args. Input args like: -# -# "text" "gcc/{9.3.0,10.3.0}" "openmpi/{4.0.5,4.1.0}" -# -# will be expanded to the following four lines: -# -# "text gcc/9.3.0 openmpi/4.0.5" -# "text gcc/9.3.0 openmpi/4.1.0" -# "text gcc/10.3.0 openmpi/4.0.5" -# "text gcc/10.3.0 openmpi/4.1.0" -# -bash_expand(){ - local text="$1" - shift - local to_expand=( "${@}" ) - if (( ${#to_expand[@]} == 0 )); then - echo ${text} - else - local list - eval list=( ${to_expand[0]} ) - local s - for s in ${list[*]}; do - bash_expand "${text} ${s}" "${to_expand[@]:1}" - done; - fi; -} - -build_modules_legacy() { - local -a files=( "${legacy_config_files[@]}" ) - local -A mod_overlays=() - - expand_variants_file(){ - local -r input="$1" - local -a toks=() - while read -a toks; do - # skip empty and comment lines - (( ${#toks[@]} == 0 )) && continue - [[ ${toks[0]:0:1} == '#' ]] && continue - local -a deps=( ${toks[*]:2} ) - bash_expand "${toks[0]} ${toks[1]}" "${deps[@]}" - done < "${input}" - } - - local name="$1" - local version="$2" - local exact_match='no' - if [[ "${version:0:1}" == "=" ]]; then - exact_match='yes' - version="${version:1}" - fi - shift 2 - local with_modules=( $* ) - - # if we have to build a dependency, we might have less dependencies - # on it. Or in other words: the list of "with modules" might be - # overdetermined. In the loop below we check, which dependencies - # specified with '--with' are required. - local m - local pattern="/^${name}\/${version}[[:blank:]]/" - for m in "${with_modules[@]}"; do - if [[ -n $(${awk} "/${m%/*}[\/ ]/" "${files[@]}") ]]; then - pattern+=" && /${m//\//\\/}/" - fi - done - - local variants=() - for f in "${files[@]}"; do - local line='' - while read line; do - variants+=( "${line}" ) - done < <(expand_variants_file "${f}" | ${awk} "${pattern}") - done - if (( ${#variants[@]} == 0 )); then - std::info "%s " \ - "${name}/${version}:" \ - "no suitable variant found!" - std::die 10 "Aborting..." - elif (( ${#variants[@]} > 1 )) && [[ ${exact_match} == 'yes' ]]; then - std::info "%s " \ - "Multiple variants found:" - for variant in "${variants[@]}"; do - std::info "${variant}" - done - std::die 10 "Aborting..." - fi - [[ -v OverlayInfo[${opt_overlay}:inst_root] ]] || \ - std::die 2 "%s" \ - "Overlay doesn't exist - ${opt_overlay}" - local ol_name="${opt_overlay}" - declare ol_inst_root="${OverlayInfo[${opt_overlay}:inst_root]}" - declare ol_mod_root="${OverlayInfo[${opt_overlay}:mod_root]}" - local -i i=0 - local -i num_variants=${#variants[@]} - for ((i = 0; i < num_variants; i++)); do - local tokens=( ${variants[i]} ) - local name="${tokens[0]%/*}" - version="${tokens[0]#*/}" - release="${tokens[1]}" - case ${release} in - unstable|stable|deprecated|remove|removed) - : - ;; - * ) - std::info "%s " \ - "${name}/${version}:" \ - "invalid release stage '${release}'!" - std::die 10 "Aborting..." - ;; - esac - with_modules=( "${tokens[@]:2}" ) - pbuild.build_module_legacy \ - "${name}" "${version}" \ - "${release}" "${with_modules[@]}" - done + yaml_config_file="${opt_config_file:-${BUILDBLOCK_DIR}/files/config.yaml}" + [[ -r "${yaml_config_file}" ]] || \ + std::die 2 \ + "%s -- %s" \ + "YAML config file doesn't exist or is not readable" \ + "${yaml_config_file}" + std::info "Using YAML configuration file - ${yaml_config_file}" } get_yaml_file_fmt(){ @@ -560,23 +371,23 @@ read_yaml_config_file() { echo "${yaml}" } -build_modules_yaml(){ +build_modules(){ local -- name="$1" local -- version="$2" shift 2 - local -a with_modules=( $* ) + local -a with_modules=( "$*" ) if [[ "${opt_check_mode}" == 'yes' ]]; then - local -- yamllint if ! which yamllint > /dev/null 2>&1; then - eval $( "${MODULECMD}" bash load yamllint/1.28.0 ) + eval $( "${modulecmd}" bash load yamllint/1.28.0 ) fi which yamllint > /dev/null 2>&1 || \ std::die 3 "yamllint not found" yamllint "${yaml_config_file}" fi - local -- file_fmt=$(get_yaml_file_fmt "${yaml_config_file}") + local -- file_fmt='' + file_fmt=$(get_yaml_file_fmt "${yaml_config_file}") local -- yaml_mod_config='' yaml_mod_config=$(read_yaml_config_file "${yaml_config_file}" "${name}") @@ -601,17 +412,17 @@ build_modules_yaml(){ # none # init_module_environment(){ - eval $( "${MODULECMD}" bash use unstable ) - eval $( "${MODULECMD}" bash use deprecated ) - eval $( "${MODULECMD}" bash purge ) + eval $( "${modulecmd}" bash use unstable ) + eval $( "${modulecmd}" bash use deprecated ) + eval $( "${modulecmd}" bash purge ) # :FIXME: this is a hack!!! # shouldn't this be set in the build-script? if [[ -e "${PMODULES_HOME%%/Tools*}/Libraries" ]]; then - eval $( "${MODULECMD}" bash use Libraries ) + eval $( "${modulecmd}" bash use Libraries ) fi if [[ -e "${PMODULES_HOME%%/Tools*}/System" ]]; then - eval $( "${MODULECMD}" bash use System ) + eval $( "${modulecmd}" bash use System ) fi unset C_INCLUDE_PATH unset CPLUS_INCLUDE_PATH @@ -636,7 +447,7 @@ init_module_environment(){ parse_version() { local v="$1" V="$1" - : ${USE_FLAGS:=''} # architectures (or empty) + USE_FLAGS=${USE_FLAGS:-''} local tmp='' @@ -667,16 +478,17 @@ parse_version() { VERSIONS=() if [[ -n ${V_RELEASE} ]]; then - VERSIONS+=( ${V_PKG}-${V_RELEASE} ) + VERSIONS+=( "${V_PKG}-${V_RELEASE}" ) fi if [[ -n ${V_PATCHLVL} ]]; then - VERSIONS+=( ${V_MAJOR}.${V_MINOR}.${V_PATCHLVL} ) + VERSIONS+=( "${V_MAJOR}.${V_MINOR}.${V_PATCHLVL}" ) fi if [[ -n ${V_MINOR} ]]; then - VERSIONS+=( ${V_MAJOR}.${V_MINOR} ) + VERSIONS+=( "${V_MAJOR}.${V_MINOR}" ) fi - VERSIONS+=( ${V_MAJOR} ) + VERSIONS+=( "${V_MAJOR}" ) } + # these variables must be export for envsubst(1) declare -x P='' declare -x V='' @@ -763,7 +575,7 @@ build_modules_yaml_v1(){ local -- name="$2" local -- version="$3" shift 3 - local -a with_modules=( $* ) + local -a with_modules=( "$@" ) check_yaml_keys(){ local -n valid_yaml_keys="$1" @@ -771,8 +583,8 @@ build_modules_yaml_v1(){ used_yaml_keys=() local -- key='' local -a keys=() - keys=( $(${yq} '.|keys().[]' 2>/dev/null) ) - debug "top-level keys: ${keys[@]}" + readarray -t keys < <( ${yq} '.|keys().[]' 2>/dev/null) + debug "top-level keys: ${keys[*]}" for key in "${keys[@]}"; do [[ -v valid_yaml_keys[${key}] ]] || \ std::die 3 "Invalid key in YAML configuration file -- ${key}" @@ -780,37 +592,6 @@ build_modules_yaml_v1(){ done } - get_value(){ - local -- yaml_input="$1" - local -n val="$2" - local -- key="$3" - local -- expected_type="$4" - - local -- type='' - type=$( ${yq} ".${key} | type" 2>/dev/null <<<"${yaml_input}") - if [[ "${type}" != "${expected_type}" ]]; then - std::die 3 "%s" \ - "Value of '${key}' must be of type '${expected_type:2}', but is '${type:2}'!" - fi - val=$( ${yq} -e ".${key}" \ - 2>/dev/null <<<"${yaml_input}" ) || val='' - } - - get_seq(){ - local -- yaml_input="$1" - local -n val="$2" - local -- key="$3" - - local -- type='' - type=$( ${yq} ".${key} | type" 2>/dev/null <<<"${yaml_input}") - if [[ "${type}" != '!!seq' ]]; then - std::die 3 "%s" \ - "Value of '${key}' must be of type 'seq', but is of type '${type:2}'!" - fi - val=$( ${yq} -e ".${key}[]" \ - 2>/dev/null <<<"${yaml_input}" ) || val='' - } - get_config(){ local -- yaml_input="$1" local -n cfg="$2" # ref. to return configuration @@ -827,9 +608,9 @@ build_modules_yaml_v1(){ fi local -a keys=() - keys=( $( ${yq} -e ".|keys().[]" <<<"${yaml_input}" 2>/dev/null )) || \ + readarray -t keys < <( ${yq} -e ".|keys().[]" <<<"${yaml_input}" 2>/dev/null ) || \ std::die 3 "Oops: retrieving keys from:\n${yaml_input}" - debug "config keys: ${keys[@]}" + debug "config keys: ${keys[*]}" for key in "${keys[@]}"; do [[ -v dfl[${key,,}] ]] || \ std::die 3 "%s -- %s\n%s" \ @@ -837,7 +618,7 @@ build_modules_yaml_v1(){ "${key}" "${yaml_input}" case ${key} in compile_in_sourcetree ) - get_value "${yaml_input}" value "${key}" '!!bool' + pm::get_value "${yaml_input}" value "${key}" '!!bool' case ${value,,} in true ) cfg[${key,,}]='yes' @@ -848,13 +629,13 @@ build_modules_yaml_v1(){ * ) std::die 3 "%s '%s' -- %s" \ "Invalid value for" \ - '${key}' \ + "${key}" \ "${value}" ;; esac ;; configure_with ) - get_value "${yaml_input}" value "${key}" '!!str' + pm::get_value "${yaml_input}" value "${key}" '!!str' case ${value,,} in auto | cmake | autotools ) cfg[${key,,}]="${value,,}" @@ -868,15 +649,15 @@ build_modules_yaml_v1(){ esac ;; default_variant | download_dir | group | overlay | script | suffix ) - get_value "${yaml_input}" value "${key}" '!!str' + pm::get_value "${yaml_input}" value "${key}" '!!str' cfg[${key,,}]="${value}" ;; group_deps ) - get_value "${yaml_input}" value "${key}" '!!map' + pm::get_value "${yaml_input}" value "${key}" '!!map' cfg[${key,,}]="${value}" ;; relstage ) - get_value "${yaml_input}" value "${key}" '!!str' + pm::get_value "${yaml_input}" value "${key}" '!!str' case ${value,,} in unstable | stable | deprecated ) cfg[${key,,}]="${value,,}" @@ -893,15 +674,15 @@ build_modules_yaml_v1(){ esac ;; urls | sub_packages ) - get_value "${yaml_input}" value "${key}" '!!seq' + pm::get_value "${yaml_input}" value "${key}" '!!seq' cfg[${key,,}]="${value}" ;; - build_requires|configure_args|docfiles|patch_files|runtime_deps|systems|target_cpus|variant ) - get_seq "${yaml_input}" value "${key}" + build_requires|configure_args|docfiles|patch_files|runtime_deps|systems|variant ) + pm::get_seq "${yaml_input}" value "${key}" cfg[${key,,}]="${value}" ;; kernels ) - get_seq "${yaml_input}" value "${key}" + pm::get_seq "${yaml_input}" value "${key}" set -o noglob local -a items=( "${value,,}" ) set +o noglob @@ -917,7 +698,7 @@ build_modules_yaml_v1(){ cfg[${key,,}]="${value}" ;; target_cpus ) - get_seq "${yaml_input}" value "${key}" + pm::get_seq "${yaml_input}" value "${key}" set -o noglob local -a items=( "${value,,}" ) set +o noglob @@ -933,7 +714,7 @@ build_modules_yaml_v1(){ cfg[${key,,}]="${value}" ;; 'configure_args+' | 'docfiles+' | 'patch_files+' ) - get_seq "${yaml_input}" value "${key}" + pm::get_seq "${yaml_input}" value "${key}" key="${key:0:-1}" if [[ -z "${cfg[${key,,}]}" ]]; then cfg[${key,,}]="${value}" @@ -946,7 +727,7 @@ build_modules_yaml_v1(){ std::die 3 "%s '%s' in %s" \ "Oops unhandled key" \ "${key}" \ - "${FUNCNAME}" + "${FUNCNAME[0]}" esac done } @@ -959,7 +740,7 @@ build_modules_yaml_v1(){ local version="$2" local -a keys=() - keys=( $( ${yq} -e '.versions|keys().[]' 2>/dev/null) ) || \ + readarray -t keys < <( ${yq} -e '.versions|keys().[]' 2>/dev/null ) || \ std::die 3 "No version keys in configuration file!" refvar=() @@ -969,7 +750,7 @@ build_modules_yaml_v1(){ for k in ${key//;/ }; do # brace expansion of key local list=() - eval list=( $k ) + list=( $(${bash} -c "echo $k") ) if [[ ${list[@]} =~ ${version} ]]; then refvar+=("${key}") break @@ -1000,7 +781,8 @@ build_modules_yaml_v1(){ } get_num_variants(){ - local -i n=$(${yq} '.|length' 2>/dev/null) + local -i n=0 + n=$(${yq} '.|length' 2>/dev/null) echo "$n" } @@ -1027,7 +809,7 @@ build_modules_yaml_v1(){ # query all specified group dependencies local -a keys=() - keys=( $(${yq} ".|keys|.[]" <<<"${yaml}" 2>/dev/null) ) + readarray -t keys < <( ${yq} ".|keys|.[]" <<<"${yaml}" 2>/dev/null ) local -- key='' for key in "${keys[@]}"; do @@ -1039,7 +821,7 @@ build_modules_yaml_v1(){ die_invalid_group_dep "${name}" "${version}" "${group}" done # are all required group dependencies defined? - for key in "${hierarchical_groups[${group,,}]}"; do + for key in ${hierarchical_groups[${group,,}]}; do is_in_array "${key,,}" "${keys[@]}" || \ die_missing_group_dep "${name}" "${version}" "${group}" done @@ -1052,11 +834,11 @@ build_modules_yaml_v1(){ local -a modules=() local keys=() - keys=( $(${yq} ".${group,,}|keys|.[]" <<<"${yaml}" 2>/dev/null) ) + readarray -t keys < <( ${yq} ".${group,,}|keys|.[]" <<<"${yaml}" 2>/dev/null ) local key for key in "${keys[@]}"; do local versions=() - versions=( $( ${yq} -e ".${group,,}.${key}[]" <<<"${yaml}" 2>/dev/null) ) + readarray -t versions < <( ${yq} -e ".${group,,}.${key}[]" <<<"${yaml}" 2>/dev/null ) local version for version in "${versions[@]}"; do if [[ -v opt_with_dict[${key}/${version}] ]]; then @@ -1165,8 +947,8 @@ build_modules_yaml_v1(){ && continue debug "build $module_name/$module_version with $compiler and $hdf5" - debug " runtime deps: ${runtime_deps[@]}" - debug " build requires: ${build_requires[@]}" + debug " runtime deps: ${runtime_deps[*]}" + debug " build requires: ${build_requires[*]}" pbuild.build_module_yaml \ "${module_name}" "${module_version}" \ "$3" \ @@ -1201,8 +983,8 @@ build_modules_yaml_v1(){ && continue debug "build $module_name/$module_version with $compiler and $mpi" - debug " runtime deps: ${runtime_deps[@]}" - debug " build requires: ${build_requires[@]}" + debug " runtime deps: ${runtime_deps[*]}" + debug " build requires: ${build_requires[*]}" pbuild.build_module_yaml \ "${module_name}" "${module_version}" \ "$3" \ @@ -1236,8 +1018,8 @@ build_modules_yaml_v1(){ for mpi in "${with_mpi[@]}"; do for hdf5 in "${with_hdf5[@]}"; do debug "build $module_name/$module_version with $compiler, $mpi and $hdf5" - debug " runtime deps: ${runtime_deps[@]}" - debug " build requires: ${build_requires[@]}" + debug " runtime deps: ${runtime_deps[*]}" + debug " build requires: ${build_requires[*]}" # build if opt_with_modules is empty or compiler is in this array (( ${#opt_with_modules[@]} != 0 )) \ @@ -1300,7 +1082,7 @@ build_modules_yaml_v1(){ local unpacker='' local key='' local value='' - while read key value; do + while read -r key value; do key=${key:0:-1} case "${key}" in url ) @@ -1359,6 +1141,12 @@ build_modules_yaml_v1(){ pbuild.add_patch_files "${args[@]}" } + die_sub_package_name_missing(){ + std::die 3 "Name of sub-package not specified in \n===\n$1\n===\n" + } + die_sub_package_version_missing(){ + std::die 3 "Version of sub-package not specified in \n===\n$1\n===\n" + } build_sub_packages(){ local -- yaml="$1" local -i l=0 @@ -1374,20 +1162,20 @@ build_modules_yaml_v1(){ local -- pkg_version='' local -a pkg_build_args=() local -a keys=() - keys=( $( ${yq} -e ".|keys().[]" <<<"${pkgs_yaml}" 2>/dev/null )) || \ + readarray -t keys < <( ${yq} -e ".|keys().[]" <<<"${pkgs_yaml}" 2>/dev/null ) || \ die_parsing "${pkgs_yaml}" local -- key='' for key in "${keys[@]}"; do case ${key,,} in 'name' ) - get_value "${pkgs_yaml}" pkg_name "${key}" '!!str' + pm::get_value "${pkgs_yaml}" pkg_name "${key}" '!!str' ;; 'version' ) - get_value "${pkgs_yaml}" pkg_version "${key}" '!!str' + pm::get_value "${pkgs_yaml}" pkg_version "${key}" '!!str' ;; 'build_args' ) local -- value='' - get_seq "${pkgs_yaml}" value "${key}" + pm::get_seq "${pkgs_yaml}" value "${key}" readarray -t pkg_build_args <<< "${value}" ;; * ) @@ -1398,6 +1186,11 @@ build_modules_yaml_v1(){ ;; esac done + [[ -n "${pkg_name}" ]] || \ + die_sub_package_name_missing "${pkgs_yaml}" + [[ -n "${pkg_version}" ]] || \ + die_sub_package_version_missing "${pkgs_yaml}" + [[ "${opt_verbose}" == 'yes' ]] && \ pkg_build_args+=( '--verbose' ) [[ "${opt_debug}" == 'yes' ]] && \ @@ -1444,10 +1237,10 @@ build_modules_yaml_v1(){ local -- kernel='' for kernel in "${kernels[@]}"; do [[ ${kernel} == 'any' ]] && return 0 - [[ ${kernel} == ${OS,,} ]] & return 0 + [[ ${kernel} == ${KernelName,,} ]] & return 0 done std::info "Skipping variant '${module_version}':" - std::info " The kernel of this systems is: ${OS}" + std::info " The kernel of this systems is: ${KernelName}" std::info " But the variant is for the following kernels: ${module_config['kernels']}" return 1 } @@ -1489,11 +1282,11 @@ build_modules_yaml_v1(){ debug "build variant ${module_name}/${module_version}" local ol_name="${module_config['overlay']}" - [[ -v OverlayInfo[${ol_name}:inst_root] ]] || \ + [[ -v OverlayInfo[${ol_name}:install_root] ]] || \ std::die 2 "%s" \ "Overlay doesn't exist - ${ol_name}" - declare ol_inst_root="${OverlayInfo[${ol_name}:inst_root]}" - declare ol_mod_root="${OverlayInfo[${ol_name}:mod_root]}" + declare ol_install_root="${OverlayInfo[${ol_name}:install_root]}" + declare ol_modulefiles_root="${OverlayInfo[${ol_name}:modulefiles_root]}" module_version+="${module_config['suffix']}" @@ -1554,7 +1347,7 @@ build_modules_yaml_v1(){ # do curly brackets expansion {} local l local list=() - eval list=( $k ) + list=( $(${bash} -c "echo $k" ) ) for l in "${list[@]}"; do if [[ $l =~ ${version} ]]; then ev_result+=("${l}") @@ -1580,15 +1373,16 @@ build_modules_yaml_v1(){ get_config "${yaml_input}" default_config Yaml_default_config if [[ -v used_keys['shasums'] ]]; then - local yaml_input=$(${yq} '.shasums' <<<"${yaml_mod_config}" 2>/dev/null) - while read key value; do + local yaml_input='' + yaml_input=$(${yq} '.shasums' <<<"${yaml_mod_config}" 2>/dev/null) + while read -r key value; do [[ -z ${key} ]] && continue SHASUMS[${key//:}]="${value}" done <<<"${yaml_input}" fi if [[ -v used_keys['type'] ]]; then local -- value='' - get_value "${yaml_mod_config}" value 'type' '!!str' + pm::get_value "${yaml_mod_config}" value 'type' '!!str' case "${value,,}" in 'module' ) [[ "${module_type}" == 'sub_package' ]] && \ @@ -1605,7 +1399,8 @@ build_modules_yaml_v1(){ fi get_matching_version_keys version_keys "${version}" <<<"${yaml_mod_config}" for version_key in "${version_keys[@]}"; do - local yaml_vk_config=$(get_yaml_vk_config "${version_key}" <<<"${yaml_mod_config}") + local yaml_vk_config='' + yaml_vk_config=$(get_yaml_vk_config "${version_key}" <<<"${yaml_mod_config}") # check keys: allowed are 'config' and 'variants' used_keys=() @@ -1621,8 +1416,10 @@ build_modules_yaml_v1(){ # reminder: if YAML input is empty, next line copies defaults to 'vk_config' get_config "${yaml_input}" vk_config default_config - local -- yaml_variants=$(get_variants <<<"${yaml_vk_config}") - local -i num_variants=$(get_num_variants <<<"${yaml_variants}") + local -- yaml_variants='' + yaml_variants=$(get_variants <<<"${yaml_vk_config}") + local -i num_variants=0 + num_variants=$(get_num_variants <<<"${yaml_variants}") local versions=() expand_version_key versions "${version_key}" "${version}" local v='' @@ -1647,15 +1444,7 @@ build_modules_yaml_v1(){ fi done done -} # build_modules_yaml() - -build_modules() { - if [[ "${opt_yaml}" == 'yes' ]]; then - build_modules_yaml "$@" - else - build_modules_legacy "$@" - fi -} +} # build_modules() debug(){ ${echo} "INFO: " "$@" 1>&2 @@ -1686,8 +1475,8 @@ pm::read_config # or # ${overlay}/var/distfiles # ? -: ${PMODULES_DISTFILESDIR:="${PMODULES_HOME%%/Tools*}/var/distfiles"} -: ${PMODULES_TMPDIR:=/var/tmp/${USER}} +PMODULES_DISTFILESDIR=${PMODULES_DISTFILESDIR:-"${PMODULES_HOME%%/Tools*}/var/distfiles"} +PMODULES_TMPDIR="${PMODULES_TMPDIR:-/var/tmp/${USER}}" export PMODULES_DISTFILESDIR PMODULES_TMPDIR declare -r BUILD_SCRIPT diff --git a/Pmodules/modmanage.bash.in b/Pmodules/modmanage.bash.in deleted file mode 100755 index 7748c11..0000000 --- a/Pmodules/modmanage.bash.in +++ /dev/null @@ -1,747 +0,0 @@ -#!@BASH@ --noprofile - -PATH='/bin:/usr/bin' -unset CDPATH # unset CDPATH, otherwise 'cd' prints the directoy! -unset IFS # use default IFS - -shopt -s nullglob -shopt -s extglob - -# used for some output only -declare -r CMD='modmanage' - -declare mydir=$(cd $(dirname "$0") && pwd) -declare prefix=$(dirname "${mydir}") -declare libdir="${prefix}/lib" -declare libexecdir="${prefix}/libexec" -declare -r bindir="${prefix}/bin" - -source "${libdir}/libstd.bash" -source "${libdir}/libpmodules.bash" - -_exit () { - std::die 1 "\nInterrupted..." -} -trap '_exit' INT TERM - -_err () { - std::info "\nOops: got an error in function '${FUNCNAME[1]}', line ${BASH_LINENO[0]}" - std::die 1 "Aborting ..." -} -trap '_err' ERR - -path="/bin:/usr/bin:${bindir}" -[[ $(uname -s) == 'Darwin' ]] && path+=":${libexecdir}" -std::def_cmds "${path}" 'chown' 'dirname' 'mkdir' 'rsync' 'rm' 'getopt' 'find' 'modulecmd' - -unset mydir -unset prefix -unset libdir -unset libexecdir -# bindir we still need - -declare PMODULES_VERSION='@PMODULES_VERSION@' - -############################################################################## -# -# help [module|sub-command] -# -Subcommands[help]='help' -Options[help]='-o hHV\? -l version -l help' -Help[help]=' -USAGE: - modmanage [switches] subcommand [subcommand-args]... - -SWITCHES: - -h|-H|-?|--help this usage info - -V|--version modules version & configuration options - --debug enable debug output - --dry-run dry run - -SUBCOMMANDS: - + init [switches] TARGET_DIR - + install [switches] module... - + search [switches] [string|pattern]... - + help [subcommand] -' - -subcommand_help() { - local -r subcommand='help' - local -a args=() - while (( $# > 0 )); do - case $1 in - -h | -\? | -H | --help ) - print_help "${subcommand}" - ;; - -V | --version ) - print_help 'version' - ;; - -- ) - shift 1 - args+=( "$@" ) - break - ;; - * ) - args+=( "$1" ) - ;; - esac - shift - done - for arg in "${args[@]}"; do - if [[ -n "${Help[${arg}]}" ]] ; then - print_help "${arg}" - else - std::die 1 "Unknown sub-command -- ${subcommand}" - fi - done -} - -############################################################################## -# -# Derive the module installation path from the modulefile path. -# The passed modulefile must be absolute. -# -# Arguments: -# $1: absolute module file path -# -get_module_prefix() { - "${modulecmd}" bash show "$1" 2>&1 \ - |awk '/_HOME |_PREFIX / {print $3; exit}' -} - -############################################################################## -# -# Derive the module release-file path from the module file-path. -# -# Arguments: -# $1: 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 -# -# Arguments: -# $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_root="$2" - local -r target_root="$3" - - local -r src_prefix=$( get_module_prefix "${src_root}/${rel_modulefile}" ) - local -r rel_prefix=${src_prefix#${src_root}/} - local -r target_prefix="${target_root}/${rel_prefix}" - - # install/update module - if [[ ! -d "${target_prefix}" ]] || [[ "${force}" == 'yes' ]]; then - ${mkdir} -p "${target_prefix}" || exit $? - ${rsync} --links --perms --recursive --delete \ - "${src_prefix}/" \ - "${target_prefix}/" || exit $? - fi - - # create modulefile direcrory and install modulefile - local -r src_modulefile="${src_root}/${rel_modulefile}" - local -r target_modulefile="${target_root}/${rel_modulefile}" - - ${mkdir} -p "$(${dirname} "${target_modulefile}")" || exit $? - - if [[ -e "${src_modulefile}" ]]; then - ${rsync} --links --perms --recursive \ - "${src_modulefile}" "${target_modulefile}" || exit $? - fi - - # install release-file - local -r rel_releasefile=$( get_releasefile_name "${rel_modulefile}" ) - local -r src_releasefile="${src_root}/${rel_releasefile}" - local -r target_releasefile="${target_root}/${rel_releasefile}" - - if [[ -e "${src_releasefile}" ]]; then - ${rsync} --links --perms --recursive \ - "${src_releasefile}" "${target_releasefile}" || exit $? - fi - -} - -############################################################################## -# -# initialize a new module environment -# -# -# -Subcommands[init]='init' -Options[init]='-o \?hfy -l src: -l user: -l help -l force -l yes' -Help[init]=" -USAGE: - modmanage init [switches] TARGET_DIR - Initialize a new minimal Pmodule environment in TARGET_DIR. - A user must be specified with '--user=' if the - programm is executed as root. - -SWITCHES: - --user - If this scripts runs with root privileges, a user name - ore ID must be specified. - --force|--yes|-f|-y - re-initialise an already existing Pmodule environment. -" - -subcommand_init() { - check_env() { - [[ -n "${PMODULES_ROOT}" ]] && - [[ -d "${PMODULES_ROOT}" ]] && - [[ -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!" - } - - #..................................................................... - # - # Sync the Pmodules configuration and templates - # - # Arguments: - # $1: source prefix of Pmodule environment - # $2: target prefix of Pmodule environment - # - sync_config() { - src="$1/${PMODULES_CONFIG_DIR}/" - dst="$2/${PMODULES_CONFIG_DIR}/" - ${rsync} --links --perms \ - "${src}"/profile.{bash,csh,zsh} "${dst}" \ - || return $? - ${rsync} --links --perms \ - "${src}"/profile.{bash,csh,zsh}-"${PMODULES_VERSION}" "${dst}" \ - || return $? - ${rsync} --links --perms \ - "${src}/Pmodules.conf" "${dst}" \ - || return $? - ${rsync} --links --perms \ - "${src}/modbuild.conf" "${dst}" \ - || return $? - } - - local target_root=() - local user='' - while (($# > 0)); do - case $1 in - -h | -H | -\? | --help | -help ) - print_help "${subcommand}" - ;; - --force | --yes | -f | -y ) - force='yes' - ;; - --user | --user=* ) - if [[ "$1" == '--user' ]]; then - user="$2" - shift - else - user="${1#--*=}" - fi - ;; - -- ) - : - ;; - * ) - # assign and remove trailing slashes - target_root="${1%%*([\/])}" - ;; - esac - shift - done - if [[ -z ${target_root} ]]; then - std::die 1 "Error: no target directory specified!" - fi - - 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 - - local src_root="$(std::get_abspath "${bindir}/../../../..")" - local config_file="${src_root}/${PMODULES_CONFIG_DIR}/profile.bash" - if [[ -r "${config_file}" ]]; then - source "${config_file}" - fi - check_env || \ - std::die 1 "Giving up..." - - echo "Creating a minimal Pmodule environment from the environment at" - echo " ${PMODULES_ROOT}" - echo "in" - echo " ${target_root}" - if [[ -d "${target_root}" ]] && [[ ${force} == no ]]; then - echo "Warning: ${target_root} 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_root}'..." - ${mkdir} -p "${target_root}" || \ - std::die 1 "Error: make directory failed!" - - echo "Syncing configuration ..." - sync_config "${src_root}" \ - "${target_root}" || \ - std::die 1 "Error: configuration synchronization failed!" - - echo "Syncing Pmodules ${PMODULES_VERSION} from '${src_root}' to '${target_root}'..." - sync_module "Tools/${PMODULES_MODULEFILES_DIR}/Pmodules/${PMODULES_VERSION}" \ - "${src_root}" \ - "${target_root}" || \ - std::die 1 "Error: sync Pmodules failed!" - - for d in "${src_root}"/*/${PMODULES_MODULEFILES_DIR}; do - ${mkdir} -p "${target_root}/${d#${src_root}/}" - done - - if [[ -n "${user}" ]]; then - echo "Changing user of new module environment to '${user}'..." - ${chown} -R "${user}" "${target_root}" || \ - std::die 1 "Error: changing owner failed!" - echo - fi - echo "SourceRoot=${src_root}" > "${target_root}/${PMODULES_CONFIG_DIR}/modmanage.conf" - echo "New minimal module environment created at '${target_root}'." - echo "To use this environment, execute" - echo " sudo ln -fs ${target_root} /opt/psi" - echo " source /opt/psi/${PMODULES_CONFIG_DIR}/profile.bash" - -} - - -############################################################################## -# -# sub-command 'install' -# -# Arguments: -Subcommands[install]='install' -Options[install]='-o hf -l force -l with: -l help -l src: -l target:' -Help[install]=' -USAGE: - modmanage install [switches] ... - Install modules - -SWITCHES: - --force] | -f - Install module even it already exists - - --src - Install from module environment in - - --with - Install module(s) in this sub-group only - - - Install modules matching given pattern -' - -subcommand_install() { - local -r subcommand='install' - local opts='' - local -a with=() - local -a module_pattern=() - local src_root="${PMODULES_INSTALL_SOURCE}" - local target_root="${PMODULES_ROOT}" - local modulefile='' - local -A modules_to_install - local -A dependencies_to_install - local -A group_map - local -a modulepath=() - - #...................................................................... - # - set_initial_modulepath() { - local group - for group in "${!GroupDepths[@]}"; do - (( ${GroupDepths[${group}]} == 0 )) || continue - modulepath+=( "${src_root}/${group}/${PMODULES_MODULEFILES_DIR}" ) - done - } - - #...................................................................... - # - create_group_map() { - # - # For the dependency resolution we need to know, whether a - # module - if loaded - adds a hierarchical group to MODULEPATH - # or not. - # - # Examples: - # Loading a compiler adds the hierarchical group for - # this compiler. The command - # module load gcc/10.3.0 - # prepends - # /Compiler/modulefiles/gcc/10.3.0 - # to MODULEPATH. - # - # The dependency files do not convey the information whether - # loading a module extends MODULEPATH or not. What we need to - # know is - # 1) does loading a specific module extends MODULEPATH? - # 2) if yes: what is the hierarchical group? - # - # Example: - # If we know that loading 'gcc/10.3.0' adds a directory - # in the hierarchical group 'Compiler' to MODULEPATH, we - # know that this directory is - # /Compiler/modulefiles/gcc/10.3.0 - # - # This information we store in the dictionary 'group_map'. - # For concinience reasons we store the string 'src_root/group'. - # So, 'group_map' maps - # module/version -> src_root/group - # - # Example: - # group_map[gcc/10.3.0]="${src_root}/Compiler" - # - local group='' - for group in "${!GroupDepths[@]}"; do - (( ${GroupDepths[${group}]} > 0 )) || continue - local fname='' - while read fname; do - local -a parts=() - std::split_relpath parts "${fname}" - if (( ${#parts[@]} - 2 != ${GroupDepths[${group}]} )); then - std::warn "error in source group ${group}:" - std::warn "modulefile: ${fname}" - continue - fi - if [[ ${#parts[@]} < 4 ]]; then - echo "${group} ${parts[@]}" - fi - local key="${parts[-4]}/${parts[-3]}" - [[ -z "${group_map[${key}]}" ]] || continue - group_map[${key}]="${src_root}/${group}" - done < <(${find} -L "${src_root}/${group}/${PMODULES_MODULEFILES_DIR}" \ - \( -type l -o -type f \) \ - \! -name ".*" \ - -printf "%P\n" \ - ) - done - } - - #...................................................................... - # - # Resolve dependencies to given module - # - # Arguments: - # $1 absolute module file name - # - # Notes: - # Following variables from the enclosing function are used: - # modulepath - # group_map (read-only) - # - resolve_dependencies () { - local -r modulefile="$1" - - local -- prefix=$(get_module_prefix "${modulefile}") - local -a rdeps=() - local -- rdeps_file="${prefix}/.dependencies" - local -a ideps=() - local -- ideps_file="${prefix}/.install_dependencies" - - if [[ -r "${rdeps_file}" ]]; then - mapfile -t rdeps < <(grep -v '^ *#' "${rdeps_file}" ) - fi - if [[ -r "${ideps_file}" ]]; then - mapfile -t ideps < <(grep -v '^ *#' "${ideps_file}" ) - fi - - # loop over all dependecies - local dep - for dep in "${rdeps[@]}" "${ideps}"; do - [[ -n ${dep} ]] || continue - # search module with current modulepath - local modulename=$(${find} "${modulepath[@]}" -path "*/${dep}" \ - -print -quit 2>/dev/null) - [[ -n ${modulename} ]] || \ - std::die 3 "Oops: required module '${dep}' not found!" - - local rel_modulename="${modulename#${src_root}/}" - dependencies_to_install[${rel_modulename}]='.' - resolve_dependencies "${modulename}" - [[ -v group_map[${dep}] ]] || continue - local dir="${group_map[${dep}]}" # = ${src_root}/ - dir+="/${rel_modulename##+([!/])/}" # += rel.name with group removed - modulepath+=( "${dir}" ) - done - } - - #...................................................................... - # - # Print list of modules which will be installed and ask user wheter - # he wants to continue or abort. - # - # Arguments: - # none - # - # Notes: - # Following variables from the enclosing function are used: - # target_root (read-only) - # modules_to_install (read-only) - # dependencies_to_install (read-only) - # - print_modules() { - local modulefile - for modulefile in "$@"; do - local -a parts - std::split_relpath parts "${modulefile}" - local s='' - if (( ${#parts[@]} >= 6 )); then - s="(${parts[2]}/${parts[3]}" - for ((i = 4; i < ${#parts[@]}-2; i+=2)); do - s+=" ${parts[i]}/${parts[i+1]}" - done - s+=')' - fi - std::info "%-20s %s" "${parts[-2]}/${parts[-1]}" "$s" - done 2>&1 | sort - } - print_modules_to_install() { - std::info "The following modules will be installed/updated:" - print_modules "${!modules_to_install[@]}" - if (( ${#dependencies_to_install[@]} > 0 )); then - std::info "\nThe following dependencies will be installed/updated:" - print_modules "${!dependencies_to_install[@]}" - fi - std::info "" - std::get_YN_answer "Do you want to continue? [n] " || \ - std::die 1 "Aborting..." - std::info "" - } - - while (($# > 0)); do - case $1 in - -h | -H | -\? | --help | -help ) - print_help "${subcommand}" - ;; - --force | -f ) - force='yes' - ;; - --src | --src=*) - if [[ $1 == --src ]]; then - src_root="$2" - shift - else - src_root="${1#--*=}" - fi - ;; - --target | --target=*) - if [[ $1 == --target ]]; then - target_root="$2" - shift - else - target_root="${1#--*=}" - fi - ;; - --with | --with=* ) - if [[ "$1" == --with ]]; then - with+=( "$2" ) - shift - else - with+=( "${1#--*=}" ) - fi - ;; - -- ) - : - ;; - * ) - module_pattern+=( "$1" ) - ;; - esac - shift - done - - if [[ -z ${src_root} ]]; then - local conf_file="${PMODULES_ROOT}/${PMODULES_CONFIG_DIR}/modmanage.conf" - if [[ -r ${conf_file} ]]; then - source "${conf_file}" - src_root="${SourceRoot}" - fi - fi - [[ -n ${src_root} ]] \ - || std::die 3 "Oops: no installation source given." - [[ -d ${src_root} ]] \ - || std::die 3 "Oops: '${src_root}' is not a valid installation source." - - source "${src_root}/${PMODULES_CONFIG_DIR}/profile.bash" - scan_groups "${src_root}" - set_initial_modulepath - create_group_map - - # search for to be installed modules and their dependencies - while read modulefile; do - modules_to_install["${modulefile#${src_root}/}"]+='.' - resolve_dependencies "${modulefile}" - done < <("${modulecmd}" bash search \ - "${module_pattern[@]}" \ - "${with[@]/#/--with=}" \ - -a --glob \ - --no-header --print-modulefiles \ - --src="${src_root}" 2>&1 1>/dev/null) - (( ${#modules_to_install[@]} == 0 )) && \ - std::die 0 "No matching modules found ..." - print_modules_to_install - - # install/update ... - for modulefile in "${!modules_to_install[@]}" "${!dependencies_to_install[@]}"; do - std::split_relpath parts "${modulefile}" - std::info " ${parts[-2]}/${parts[-1]}" - sync_module "${modulefile}" "${src_root}" "${target_root}" - done - std::info "\nDone!\n" -} # subcommand_install - -############################################################################## -# -# sub-command 'search' -# -Subcommands[search]='search' -Options[search]='-o \?h -l with: -l help -l all-dep -l wrap -l glob -l src:' -Help[install]=' -USAGE: - modmanage search [switches] ... - search modules - -SWITCHES: - --src - Search modules in environment . - Default is the source defined in modmanage.conf. - - --with - Search module(s) in this sub-group. - - - Search modules matching given string. - - - Search modules matching given shell glob-pattern. -' - -subcommand_search() { - local -a args=() - while (($# > 0)); do - case $1 in - -h | -H | -\? | --help | -help ) - print_help "${subcommand}" - ;; - --src | --src=*) - if [[ $1 == --src ]]; then - src_root="$2" - shift - else - src_root="${1#--*=}" - fi - ;; - --with | --with=* ) - if [[ "$1" == --with ]]; then - args+=( '--with' "$2" ) - shift - else - args+=( "$1" ) - fi - ;; - --all-deps | --glob | --wrap ) - args+=( "$1" ) - ;; - -- ) - : - ;; - * ) - args+=( "$1" ) - ;; - esac - shift - done - if [[ -z ${src_root} ]]; then - local conf_file="${PMODULES_ROOT}/${PMODULES_CONFIG_DIR}/modmanage.conf" - if [[ -r ${conf_file} ]]; then - source "${conf_file}" - src_root="${SourceRoot}" - fi - fi - [[ -n ${src_root} ]] \ - || std::die 3 "Oops: no installation source given." - [[ -d ${src_root} ]] \ - || std::die 3 "Oops: '${src_root}' is not a valid installation source." - ${modulecmd} bash search --src="${src_root}" --all-release-stages \ - "${args[@]}" 2>&1 1>/dev/null -} - -declare force='no' -declare subcommand='' -declare -a opts=() -while (($# > 0)); do - case $1 in - -h | -H | -\? | --help | -help ) - print_help 'help' - ;; - -V | --version ) - print_help 'version' - ;; - --debug ) - set -x - ;; - --dry-run ) - chown="echo ${chown}" - mkdir="echo ${mkdir}" - rsync="echo ${rsync}" - ;; - -* ) - opts+=( "$1" ) - ;; - * ) - subcommand="$1" - shift - break - ;; - esac - shift || : -done - -if [[ -z "${subcommand}" ]]; then - std::die 1 "${CMD}: no sub-command specified.\n" - print_help 'help' -fi - -if [[ -z "${Subcommands[${subcommand}]}" ]]; then - std::die 1 "${CMD}: unknown sub-command -- ${subcommand}\n" -fi - -if [[ "${subcommand}" != "init" ]] && [[ -z "${PMODULES_ROOT}" ]]; then - std::die 1 "Error: No current module environment configured!" -fi - -tmp=$("${getopt}" --name="${CMD}" ${Options[${subcommand}]} -- "${opts[@]}" "$@" ) \ - || print_help "${subcommand}" -eval args=( "$tmp" ) -unset tmp - -umask 022 - -subcommand_${Subcommands[$subcommand]} "${args[@]}" - -# Local Variables: -# mode: sh -# sh-basic-offset: 8 -# tab-width: 8 -# End: diff --git a/Pmodules/modmanage.in b/Pmodules/modmanage.in deleted file mode 100644 index 6925f9f..0000000 --- a/Pmodules/modmanage.in +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh - -unset BASH_ENV - -declare mydir=$(cd $(dirname "$0") && pwd) -declare libexecdir="$(dirname "${mydir}")/libexec" - -declare bash="${libexecdir}/bash" -declare modmanage="${libexecdir}/modmanage.bash" - -"${bash}" --noprofile --norc "${modmanage}" "$@" diff --git a/Pmodules/modulecmd.bash.in b/Pmodules/modulecmd.bash.in index 7b6e70c..be9e169 100644 --- a/Pmodules/modulecmd.bash.in +++ b/Pmodules/modulecmd.bash.in @@ -1,46 +1,45 @@ #!@BASH@ --noprofile # +# shellcheck -x -e SC1008,SC2239,SC2317,SC2034,SC2128,SC2059,SC2178 +# +declare -r VERSION='@PMODULES_VERSION@' unset CDPATH # unset CDPATH, otherwise 'cd' prints the directoy! unset IFS # use default IFS +#set -o errexit +set -o pipefail +set -o nounset +#shopt -s nocaseglob +#shopt -s extglob shopt -s nullglob -#set -e # exit on error -#set -u # Treat unset variables as an error -set -o pipefail -# used in some output messages only -declare -r CMD='module' -declare subcommand='' +# get absolute path of script +mydir=$(cd "$(/usr/bin/dirname "$0")" && pwd -L) +prefix=$(/usr/bin/dirname "${mydir}"); -declare -r mydir="$(cd $(/usr/bin/dirname "$0") && pwd)" -declare -- prefix="$(/usr/bin/dirname "${mydir}")" -declare -r libdir="${prefix}/lib" -declare -r libexecdir="${prefix}/libexec" +path_orig="${PATH}" +PATH="${prefix}/bin:${prefix}/libexec:/bin:/usr/bin:/sbin:/usr/sbin" +source "${prefix}/lib/libstd.bash" || { + echo "Oops: cannot source library -- '$_'" 1>&2; exit 3; +} +source "${prefix}/lib/libpmodules.bash" || \ + std::die 3 "Oops: cannot source library -- '$_'" -source "${libdir}/libstd.bash" -source "${libdir}/libpmodules.bash" - -declare -r os_name=$(uname -s) -declare -r os_release=$(std::get_os_release) - -path="${libexecdir}:/bin:/usr/bin" -std::def_cmds "${path}" \ - 'awk' 'base64' 'dirname' 'find' 'getopt' 'logger' 'mktemp' \ - 'rm' 'rmdir' 'sed' 'sort' 'tput' 'yq' - -declare -rx TCL_LIBRARY="${PMODULES_HOME}/lib/tcl@TCL_VERSION@" +declare -rx TCL_LIBRARY="${prefix}/lib/tcl@TCL_VERSION@" declare -x TCLLIBPATH=${TCLLIBPATH:-''} -std::prepend_path TCLLIBPATH "${PMODULES_HOME}/lib/Pmodules" +std::prepend_path TCLLIBPATH "${prefix}/lib/Pmodules" +declare -r Tcl_cmd="${prefix}/libexec/modulecmd.bin" +declare -r Lmod_cmd="${prefix}/libexec/lmod/lmod/libexec/lmod" +declare -- modulecmd="${Tcl_cmd}" -declare -r tcl_cmd="${libexecdir}/modulecmd.bin" -declare -r lmod_cmd="${PMODULES_HOME}/libexec/lmod/lmod/libexec/lmod" -declare -- modulecmd="${tcl_cmd}" - -declare -- verbosity_lvl=${PMODULES_VERBOSITY:-'verbose'} - -declare -- Shell='' +# we have to use the orignal path. Otherwise module load doesn't work. +PATH="${path_orig}" +unset path_orig +unset mydir +unset prefix +############################################################################## # the following settings are used if the config file doesn't exist # set groups which should be available after initialization @@ -52,46 +51,65 @@ declare -- ReleaseStages=':unstable:stable:deprecated:' # set releases which should be available after initialization declare -- DefaultReleaseStages='stable' -declare -- SysCollectionsDir='/opt/psi/collections' +declare -A OverlayExcludes=() +declare -a UsedOverlays=() -# not used here but in modbuild -declare -- TmpDir="/opt/psi/var/tmp/${USER}" -declare -- DistfilesDir="/opt/psi/var/distfiles" +############################################################################## +declare -- Verbosity_lvl='verbose' +declare -- Shell='' +TmpFile=$( ${mktemp} /tmp/Pmodules.XXXXXX ) \ + || std::die 1 "Oops: unable to create tmp file!" +declare -r TmpFile -declare -A OverlayExcludes +declare -A Subcommands=() +declare -A Options=() +declare -A Help=() + +# initialize help text of 'module --version' +Help['version']=" +Pmodules @PMODULES_VERSION@ +using Tcl Environment Modules +VERSION = @MODULES_VERSION@ +" + +# +# display help text for command given in $1 +# +print_help() { + echo -e "${Help[$1]}" 1>&2 + std::die 1 +} export_env() { - case "${Shell}" in - sh | bash | zsh ) - local -r fmt="export %s=\"%s\"; " - ;; - csh | tcsh ) - local -r fmt="setenv %s \"%s\"; " - ;; - python ) - local -r fmt="os.environ['%s'] = '%s'\n" - ;; - * ) - std::die 1 "Unsupported shell -- ${Shell}" - ;; - esac + local -A export_functions=() + export_functions['sh']='export_env_sh' + export_functions['bash']='export_env_sh' + export_functions['zsh']='export_env_sh' + export_functions['csh']='export_env_csh' + export_functions['tcsh']='export_env_csh' + export_functions['python']='export_env_python' - while (( $# > 0 )); do - printf "${fmt}" "$1" "${!1}" - shift - done - # :FIXME: UsedGroups can be modified in libmodule.tcl using - # append-path/remove-path! But we keep the state in PMODULES_ENV - # so we don't have to export it. - # - #case "${Shell}" in - #sh | bash | zsh ) - # echo "unset UsedGroups; " - # ;; - #csh | tcsh ) - # echo "unsetenv UsedGroups; " - # ;; - #esac + export_env_sh(){ + while (( $# > 0 )); do + printf "export %s=\"%s\"; " "$1" "${!1}" + shift + done + } + export_env_csh(){ + while (( $# > 0 )); do + printf "setenv %s \"%s\"; " "$1" "${!1}" + shift + done + } + export_env_python(){ + while (( $# > 0 )); do + printf "os.environ['%s'] = '%s'\n" "$1" "${!1}" + shift + done + } + [[ -v export_functions[${Shell}] ]] || \ + std::die 1 "Unsupported shell -- ${Shell}" + ${export_functions[${Shell}]} "$@" } # @@ -102,10 +120,13 @@ export_env() { # none # -declare g_env_must_be_saved='no' +declare EnvMustBeSaved='no' save_env() { encode_base64(){ + local -- os_name='' + os_name=$(${uname} -s) + case "${os_name}" in Linux ) "${base64}" --wrap=0 <<< "$1" @@ -120,7 +141,7 @@ save_env() { esac } - [[ $1 == 'no' ]] && return 0 + [[ "${EnvMustBeSaved}" == 'no' ]] && return 0 local vars=( Version ) vars+=( UsedReleaseStages UsedGroups ) vars+=( DefaultGroups DefaultReleaseStages ) @@ -131,8 +152,10 @@ save_env() { vars+=( OverlayExcludes ) vars+=( OverlayInfo Dir2OverlayMap) - local s=$(typeset -p ${vars[@]}) - declare -gx PMODULES_ENV=$( encode_base64 "$s" ) + local s='' + s=$(typeset -p "${vars[@]}") + declare -gx PMODULES_ENV='' + PMODULES_ENV=$( encode_base64 "$s" ) export_env 'PMODULES_ENV' } @@ -140,137 +163,132 @@ save_env() { # function called on exit # _exit() { - save_env "${g_env_must_be_saved}" - if [[ -n "${tmpfile}" ]] && [[ -e "${tmpfile}" ]]; then - ${rm} -f "${tmpfile}" || : + save_env + if [[ -n "${TmpFile}" ]] && [[ -e "${TmpFile}" ]]; then + ${rm} -f "${TmpFile}" || : fi } trap '_exit' EXIT +declare -r CMD='module' +declare SubCommand='' die_missing_arg(){ std::die 3 "%s %s: %s\n" \ - "${CMD}" "${subcommand}" 'missing argument' + "${CMD}" "${SubCommand}" 'missing argument' } die_too_many_args(){ std::die 3 "%s %s: %s\n" \ - "${CMD}" "${subcommand}" 'too many arguments' + "${CMD}" "${SubCommand}" 'too many arguments' } die_no_args_allowed(){ std::die 3 "%s %s: %s\n" \ - "${CMD}" "${subcommand}" "no arguments allowed" + "${CMD}" "${SubCommand}" "no arguments allowed" } die_wrong_number_of_args(){ std::die 1 "%s %s: %s\n" \ - "${CMD}" "${subcommand}" "wrong number of arguments" + "${CMD}" "${SubCommand}" "wrong number of arguments" } die_illegal_opt(){ std::die 3 "%s %s: %s -- %s\n" \ - "${CMD}" "${subcommand}" "illegal option" "$1" + "${CMD}" "${SubCommand}" "illegal option" "$1" } die_illegal_arg(){ std::die 3 "%s %s: %s -- %s\n" \ - "${CMD}" "${subcommand}" "invalid argument" "$1" + "${CMD}" "${SubCommand}" "invalid argument" "$1" } die_illegal_group(){ std::die 3 "%s %s: %s -- %s\n" \ - "${CMD}" "${subcommand}" "invalid group name" "$1" + "${CMD}" "${SubCommand}" "invalid group name" "$1" } die_illegal_relstage(){ std::die 3 "%s %s: %s -- %s\n" \ - "${CMD}" "${subcommand}" "invalid release stage" "$1" + "${CMD}" "${SubCommand}" "invalid release stage" "$1" } die_cannot_remove_grp(){ std::die 3 "%s %s: %s -- %s\n" \ - "${CMD}" "${subcommand}" "cannot remove group due to loaded modules" "$1" + "${CMD}" "${SubCommand}" "cannot remove group due to loaded modules" "$1" } die_module_unavail(){ std::die 3 "%s %s: %s -- %b\n" \ - "${CMD}" "${subcommand}" "not available in the current MODULEPATH" "$1" + "${CMD}" "${SubCommand}" "not available in the current MODULEPATH" "$1" } die_module_nexist(){ std::die 3 "%s %s: module does not exist -- %s" \ - "${CMD}" "${subcommand}" "$1" + "${CMD}" "${SubCommand}" "$1" } die_invalid_value(){ std::die 1 "%s %s: %s -- %s\n" \ - "${CMD}" "${subcommand}" "invalid string for $1" "$2" + "${CMD}" "${SubCommand}" "invalid string for $1" "$2" } die_conflict(){ std::die 3 "%s %s: %s -- %b\n" \ - "${CMD}" "${subcommand}" \ + "${CMD}" "${SubCommand}" \ "module conflicts with already loaded modules" "$1" } die_cmd_failed(){ - local error_txt='failed' std::die 3 "%s %s: %s" \ - "${CMD}" "${subcommand}" \ + "${CMD}" "${SubCommand}" \ "failed" } die_cannot_use_overlay(){ std::die 3 "%s %s: %s -- %s" \ - "${CMD}" "${subcommand}" \ + "${CMD}" "${SubCommand}" \ "overlay cannot be added since some modules are already loaded!" "$1" } die_overlay_already_in_use(){ std::die 3 "%s %s: %s -- %s" \ - "${CMD}" "${subcommand}" \ + "${CMD}" "${SubCommand}" \ "overlay already in use" "$1" } die_not_a_modulefile(){ std::die 3 "%s %s: %s -- %s" \ - "${CMD}" "${subcommand}" \ + "${CMD}" "${SubCommand}" \ "not a modulefile" "$1" } -die_invalid_collection_name(){ - std::die 3 "%s %s: %s -- %s" \ - "${CMD}" "${subcommand}" \ - "invalid collection name" "$1" -} - die_cannot_create_directory(){ std::die 3 "%s %s: %s -- %s" \ - "${CMD}" "${subcommand}" \ + "${CMD}" "${SubCommand}" \ "cannot create directory" "$1" } die_cannot_save_collection(){ std::die 3 "%s %s: %s -- %s" \ - "${CMD}" "${subcommand}" \ + "${CMD}" "${SubCommand}" \ "cannot save_collection" "$1" } die_invalid_collection_name(){ std::die 3 "%s %s: %s -- %s" \ - "${CMD}" "${subcommand}" \ + "${CMD}" "${SubCommand}" \ "invalid collection name" "$1" } die_collection_doesnt_exist(){ std::die 3 "%s %s: %s -- %s" \ - "${CMD}" "${subcommand}" \ + "${CMD}" "${SubCommand}" \ "collection doesn't exist or isn't readable" "$1" } die_removing_collection_failed(){ std::die 3 "%s %s: %s -- %s" \ - "${CMD}" "${subcommand}" \ + "${CMD}" "${SubCommand}" \ "cannot remove collection" "$1" } @@ -296,16 +314,19 @@ get_module_config(){ - the release stage of a module inside out hierarchy without a config file is always 'unstable'. + Arguments: + $1 reference to dictionary to return the configuration + $2 directory containing modulefile + $3 module name (inkl. version and/or sub-dirs) " local -n ref_cfg="$1" local -r dir="$2" local -r modulefile="${dir}/$3" - debug "modulefile: $modulefile" ref_cfg['relstage']='unstable' ref_cfg['systems']='' ref_cfg['blocklist']='' - if [[ ! -v Dir2OverlayMap[${dir%/${PMODULES_MODULEFILES_DIR}*}] ]]; then + if [[ ! -v Dir2OverlayMap[${dir%/"${PMODULES_MODULEFILES_DIR}"*}] ]]; then # this module is not in an overlay ref_cfg['relstage']='stable' return @@ -313,10 +334,11 @@ get_module_config(){ local -r config_file="${modulefile%/*}/.config-${modulefile##*/}" local -r relstage_file="${modulefile%/*}/.release-${modulefile##*/}" if [[ -r ${config_file} ]]; then - local -- yaml=$(${yq} -e '.' < "${config_file}") - debug "yaml: ${yaml}" + local -- yaml='' + yaml=$(${yq} -e '.' < "${config_file}") + debug "module config: ${yaml}" local -- key='' - for key in ${!ref_cfg[@]}; do + for key in "${!ref_cfg[@]}"; do case "${key,,}" in systems | blocklist ) value=$( ${yq} -e ".${key}[]" \ @@ -350,17 +372,17 @@ is_available(){ " local -n ref_cfg="$1" local -- relstages="$2" - local -- system="$3" - local -- hostname="$4" + local -- os_release='' + os_release=$(std::get_os_release) check_relstage(){ - [[ ":${relstages}:" =~ ":${ref_cfg['relstage']}:" ]] + [[ ":${relstages}:" == *:${ref_cfg['relstage']}:* ]] } check_blocklist(){ [[ -z ${ref_cfg['blocklist']} ]] && return 0 local -- s='' for s in ${ref_cfg['blocklist']}; do - if [[ "${system}" == $s ]] || [[ "${HOSTNAME}" == $s ]]; then + if [[ "${os_release}" =~ $s ]] || [[ "${HOSTNAME}" =~ $s ]]; then return 0 fi done @@ -370,7 +392,7 @@ is_available(){ [[ -z ${ref_cfg['systems']} ]] && return 0 local -- s='' for s in ${ref_cfg['systems']}; do - if [[ "${system}" == $s ]] || [[ "${HOSTNAME}" == $s ]]; then + if [[ "${os_release}" =~ $s ]] || [[ "${HOSTNAME}" =~ $s ]]; then return 0 fi done @@ -404,13 +426,13 @@ find_overlay () { local -n fo_group="$2" local path="${3//+(\/)/\/}" # replace multpile '/' with one path="${path/%\/}" # remove trailing slash if exist - path="${path%/${PMODULES_MODULEFILES_DIR}*}" + path="${path%/"${PMODULES_MODULEFILES_DIR}"*}" # return if not in an overlay [[ -v Dir2OverlayMap[${path}] ]] || return 1 fo_ol="${Dir2OverlayMap[${path}]}" - fo_group="${path#${OverlayInfo[${ol}:mod_root]}/}" + fo_group="${path#"${OverlayInfo[${ol}:modulefiles_root]}"/}" return 0 } @@ -450,7 +472,7 @@ is_modulefile() { return 0 fi local -- shebang - read -n 11 shebang < "${fname}" + read -r -n 11 shebang < "${fname}" if [[ "${shebang:0:8}" == '#%Module' ]] \ || [[ "${shebang:0:9}" == '#%Pmodule' ]]; then im_interp='tcl' @@ -489,7 +511,7 @@ subcommand_generic0() { while (( $# > 0 )); do case $1 in -\? | -H | --help ) - print_help "${subcommand}" + print_help "${SubCommand}" ;; -- ) shift 1 @@ -504,7 +526,7 @@ subcommand_generic0() { done (( ${#args[@]} == 0 )) || \ die_no_args_allowed - "${modulecmd}" "${Shell}" "${subcommand}" + "${modulecmd}" "${Shell}" "${SubCommand}" } subcommand_generic1() { @@ -512,7 +534,7 @@ subcommand_generic1() { while (( $# > 0 )); do case $1 in -\? | -H | --help ) - print_help "${subcommand}" + print_help "${SubCommand}" ;; -- ) shift 1 @@ -529,7 +551,7 @@ subcommand_generic1() { die_missing_arg (( ${#args[@]} > 1 )) && \ die_too_many_args - "${modulecmd}" "${Shell}" "${subcommand}" "${args[@]}" + "${modulecmd}" "${Shell}" "${SubCommand}" "${args[@]}" } subcommand_generic1plus() { @@ -537,7 +559,7 @@ subcommand_generic1plus() { while (( $# > 0 )); do case $1 in -\? | -H | --help ) - print_help "${subcommand}" + print_help "${SubCommand}" ;; -- ) shift 1 @@ -552,7 +574,7 @@ subcommand_generic1plus() { done (( ${#args[@]} == 0 )) && \ die_missing_arg - "${modulecmd}" "${Shell}" "${subcommand}" "${args[@]}" + "${modulecmd}" "${Shell}" "${SubCommand}" "${args[@]}" } ############################################################################## @@ -561,9 +583,9 @@ subcommand_generic1plus() { # # $1: module to load # -Subcommands[add]='load' -Subcommands[load]='load' -Options[load]='-l help -o \?Hfsvw -l force -l silent -l verbose -l warn' +Subcommands['add']='load' +Subcommands['load']='load' +Options['load']='-l help -o \?Hfsvw -l force -l silent -l verbose -l warn' Help[load]=' USAGE: module add modulefile... @@ -591,19 +613,19 @@ subcommand_load() { local -n output="$1" # ref.var to store result local relstage='' output='' - while read -a line; do - [[ -z ${line} ]] && continue + while read -r -a line; do + (( ${#line[@]} == 0 )) && continue relstage=${line[1]} - if [[ ! ":${UsedReleaseStages}:" =~ "${relstage}" ]]; then + if [[ ! ":${UsedReleaseStages}:" == *:${relstage}:* ]]; then output+="module use ${relstage}; " fi local group=${line[2]} - if [[ ! ":${UsedGroups}:" =~ ":${group}:" ]] && \ + if [[ ! ":${UsedGroups}:" == *:${group}:* ]] && \ (( ${GroupDepths[${group}]} == 0 )); then output+="module use ${group}; " fi local -i n=$(( ${GroupDepths[${group}]}/2 )) - output+="module load ${line[@]:4:$n} ${line[0]}\n" + output+="module load ${line[*]:4:$n} ${line[0]}\n" done < <(set +x; subcommand_search "${m}" -a --no-header 2>&1) if [[ -n ${output} ]]; then output="\n\nTry with one of the following command(s):\n${output}" @@ -618,7 +640,8 @@ subcommand_load() { #...................................................................... load_dependencies() { local -r fname="$1" - while read dep; do + local -- dep='' + while read -r dep; do [[ -z ${dep} ]] && continue [[ ${dep:0:1} == \# ]] && continue module_is_loaded "${dep}" && continue @@ -650,8 +673,8 @@ subcommand_load() { local -r group="$2/${PMODULES_MODULEFILES_DIR}" local ol for ol in "${UsedOverlays[@]}"; do - local inst_root="${OverlayInfo[${ol}:inst_root]}" - if [[ -d "${inst_root}/${group}" ]]; then + local install_root="${OverlayInfo[${ol}:install_root]}" + if [[ -d "${install_root}/${group}" ]]; then _ol="${ol}" return 0 fi @@ -665,11 +688,11 @@ subcommand_load() { local ol='' find_overlay_with_group ol "${group}" || return 1 - local moduledir="${OverlayInfo[${ol}:mod_root]}/${group}/${PMODULES_MODULEFILES_DIR}" + local moduledir="${OverlayInfo[${ol}:modulefiles_root]}/${group}/${PMODULES_MODULEFILES_DIR}" local -i depth compute_group_depth depth "${moduledir}" || return 1 GroupDepths[${group}]=${depth} - g_env_must_be_saved='yes' + EnvMustBeSaved='yes' } #...................................................................... @@ -679,19 +702,19 @@ subcommand_load() { while (($# > 0)); do case $1 in -\? | -H | --help ) - print_help "${subcommand}" + print_help "${SubCommand}" ;; -f | --force ) opts+=(' -f') ;; -s | --silent ) - verbosity_lvl='silent' + Verbosity_lvl='silent' ;; -v | --verbose ) - verbosity_lvl='verbose' + Verbosity_lvl='verbose' ;; -w | --warn ) - verbosity_lvl='warn' + Verbosity_lvl='warn' ;; -- ) shift 1 @@ -699,7 +722,7 @@ subcommand_load() { break ;; * ) - args+=( $1 ) + args+=( "$1" ) ;; esac shift @@ -710,7 +733,7 @@ subcommand_load() { local m='' for m in "${args[@]}"; do IFS=':' read -r -a modulepath <<< "${MODULEPATH}" - if [[ "$m" =~ ":" ]]; then + if [[ "$m" == *:* ]]; then # extendet module name is either # - group:name or @@ -719,10 +742,8 @@ subcommand_load() { # - relstage:group:name or # - name:stage - - IFS=':' - local -a toks=($m) - unset IFS + local -a toks=() + IFS=':' read -r -a toks <<< "${m}" local group='' local relstage='' if is_group "${toks[0]}"; then @@ -756,15 +777,15 @@ subcommand_load() { die_illegal_group "${group}" group+="/${PMODULES_MODULEFILES_DIR}" for overlay in "${UsedOverlays[@]}"; do - local mod_root="${OverlayInfo[${overlay}:mod_root]}" - modulepath=( "${mod_root}/${group}" "${modulepath[@]}" ) + local modulefiles_root="${OverlayInfo[${overlay}:modulefiles_root]}" + modulepath=( "${modulefiles_root}/${group}" "${modulepath[@]}" ) done fi if [[ -n ${relstage} ]]; then is_release_stage "${relstage}" || \ die_illegal_relstage "${relstage}" std::append_path UsedReleaseStages "${relstage}" - g_env_must_be_saved='yes' + EnvMustBeSaved='yes' fi fi # handle extended module names local moduledir='' @@ -781,7 +802,7 @@ subcommand_load() { [[ ${m} == Pmodules/* ]] && [[ -n ${LOADEDMODULES} ]] && \ die_conflict "${m}" # continue if already loaded - [[ ":${LOADEDMODULES}:" =~ ":${m}:" ]] && continue + [[ ":${LOADEDMODULES}:" == *:${m}:* ]] && continue local interp='' is_modulefile interp "${current_modulefile}" || \ @@ -795,12 +816,13 @@ subcommand_load() { if [[ "${interp}" == 'lmod' ]]; then current_modulefile="${current_modulefile/${moduledir}\/}" - modulecmd="${lmod_cmd}" + modulecmd="${Lmod_cmd}" else - modulecmd="${tcl_cmd}" + modulecmd="${Tcl_cmd}" fi - local output=$("${modulecmd}" 'bash' "${opts[@]}" 'load' \ - "${current_modulefile}" 2> "${tmpfile}") + local output='' + output=$("${modulecmd}" 'bash' "${opts[@]}" 'load' \ + "${current_modulefile}" 2> "${TmpFile}") # we do not want to print the error message we got from # modulecmd, they are a bit ugly @@ -809,8 +831,9 @@ subcommand_load() { # modulecmd, but not the output to stderr coded in a # modulefile. - local error=$( < "${tmpfile}") - if [[ "${error}" =~ ":ERROR:" ]]; then + local error='' + error=$( < "${TmpFile}") + if [[ "${error}" == *:ERROR:* ]]; then local s=${error%%$'\n'*} [[ "$s" =~ ' conflicts ' ]] && \ die_conflict "${m}" @@ -829,7 +852,7 @@ subcommand_load() { echo "${error}" 1>&2 fi local msg='' - if [[ ${verbosity_lvl} != silent ]] && \ + if [[ ${Verbosity_lvl} != silent ]] && \ [[ ${relstage} != stable ]]; then msg=$(printf "%s %s: %s -- %s" \ "${CMD}" 'load' \ @@ -847,14 +870,14 @@ subcommand_load() { # fix LOADEDMODULES LOADEDMODULES="${_LMFILES_}" local dir - while read dir; do + while read -r dir; do # if the first or last character of MODULEPATH is ':', # we read an empty string. [[ -z ${dir} ]] && continue [[ "${dir: -1}" == "/" ]] || dir+="/" LOADEDMODULES="${LOADEDMODULES//${dir}}" done <<< "${MODULEPATH//:/$'\n'}" - g_env_must_be_saved='yes' + EnvMustBeSaved='yes' export_env 'LOADEDMODULES' } @@ -862,9 +885,9 @@ subcommand_load() { # # unload # -Subcommands[rm]='unload' -Subcommands[unload]='unload' -Options[unload]='-o \?H -l help' +Subcommands['rm']='unload' +Subcommands['unload']='unload' +Options['unload']='-o \?H -l help' Help[unload]=" USAGE: module rm modulefile... @@ -883,7 +906,7 @@ subcommand_unload() { while (( $# > 0 )); do case $1 in -\? | -H | --help ) - print_help "${subcommand}" + print_help "${SubCommand}" ;; -- ) shift 1 @@ -911,7 +934,7 @@ subcommand_unload() { # the loop again, if it has been unset. local saved_home="${PMODULES_HOME}" - IFS=: read -r -a _lmfiles_ <<< "${_LMFILES_}" + IFS=':' read -r -a _lmfiles_ <<< "${_LMFILES_}" local arg local lmfile for arg in "${args[@]}"; do @@ -927,12 +950,13 @@ subcommand_unload() { is_modulefile interp "${lmfile}" || die_not_a_modulefile "${lmfile}" if [[ "${interp}" == 'lmod' ]]; then current_modulefile="${current_modulefile/${moduledir}\/}" - modulecmd="${lmod_cmd}" + modulecmd="${Lmod_cmd}" else - modulecmd="${tcl_cmd}" + modulecmd="${Tcl_cmd}" fi - local output=$("${modulecmd}" "${Shell}" 'unload' "${arg}") + local output='' + output=$("${modulecmd}" "${Shell}" 'unload' "${arg}") eval "$(echo "${output}"|${sed} -e 's/;unalias [^;]*//g')" case ${Shell} in sh | bash | zsh ) @@ -947,17 +971,17 @@ subcommand_unload() { PMODULES_HOME="${saved_home}" export_env 'PMODULES_HOME' fi - g_env_must_be_saved='yes' + EnvMustBeSaved='yes' } # subcommand_unload ############################################################################## # # swap [] # -Subcommands[switch]='swap' -Subcommands[swap]='swap' -Options[swap]='-o \?H -l help' -Help[swap]=" +Subcommands['switch']='swap' +Subcommands['swap']='swap' +Options['swap']='-o \?H -l help' +Help['swap']=" USAGE: module switch [modulefile1] modulefile2 module swap [modulefile1] modulefile2 @@ -971,7 +995,7 @@ subcommand_swap() { while (( $# > 0 )); do case $1 in -\? | -H | --help ) - print_help "${subcommand}" + print_help "${SubCommand}" ;; -- ) shift 1 @@ -1004,10 +1028,10 @@ subcommand_swap() { # # show # -Subcommands[display]='show' -Subcommands[show]='show' -Options[show]='-o \?H -l help' -Help[show]=' +Subcommands['display']='show' +Subcommands['show']='show' +Options['show']='-o \?H -l help' +Help['show']=' USAGE: module display modulefile... module show modulefile... @@ -1023,7 +1047,7 @@ subcommand_show() { while (( $# > 0 )); do case $1 in -\? | -H | --help ) - print_help "${subcommand}" + print_help "${SubCommand}" ;; -- ) shift 1 @@ -1041,7 +1065,7 @@ subcommand_show() { local arg for arg in "${args[@]}"; do - "${modulecmd}" "${Shell}" "${subcommand}" "${arg}" + "${modulecmd}" "${Shell}" "${SubCommand}" "${arg}" done } @@ -1072,7 +1096,7 @@ get_available_modules() { # loop over all entries in given module path for dir in "$@"; do test -d "${dir}" || continue - cd "${dir}" + cd "${dir}" || std::die 3 "Oops: cannot change to directory '${dir}'" # find overlay and group for this directory local ol='' local group='' @@ -1086,7 +1110,7 @@ get_available_modules() { # loop over all files (and sym-links) in this directory and # its sub-directories local mod='' # module_name/module_version - while read mod; do + while read -r mod; do local name="${mod%/*}" [[ -v OverlayExcludes[${name}] ]] && continue local add='no' @@ -1117,7 +1141,7 @@ get_available_modules() { [[ "${add}" == 'no' ]] && continue local -A cfg=() get_module_config cfg "${dir}" "${mod}" - is_available cfg "${used_relstages}" "${os_release}" "${HOSTNAME}" || continue + is_available cfg "${used_relstages}" || continue gam_mods+=( "${mod}" "${cfg['relstage']}" "${dir}/${mod}" "${ol}" ) dict[${sdirs}/${mod}]=1 @@ -1158,21 +1182,20 @@ find_modulefile() { # a version number has been specified. The first module # found will be returned, independend from the release # stage. - modules=( $(${find} -L "${dir}" -type f -not -name ".*" \ + mapfile -t modules < <( ${find} -L "${dir}" -type f -not -name ".*" \ -ipath "${dir}/${module}*" \ - | cut -b${col}-) ) + | cut -b${col}-) for mod in "${modules[@]}"; do - if [[ ${mod} == ${module} ]]; then + if [[ ${mod} == "${module}" ]]; then : - elif [[ ${mod} == ${module}.lua ]]; then + elif [[ ${mod} == "${module}.lua" ]]; then mod="${module}.lua" else continue fi local -A cfg=() get_module_config cfg "${dir}" "${mod}" - is_available cfg "${ReleaseStages}" "${os_release}" \ - "${HOSTNAME}" || continue + is_available cfg "${ReleaseStages}" || continue fm_modulefile="${dir}/${mod}" fm_relstage="${cfg['relstage']}" @@ -1186,10 +1209,10 @@ find_modulefile() { # get list of reverse sorted version numbers (( col += ${#module} + 1 )) - modules=( $(${find} -L "${dir}" -type f -not -name ".*" \ + mapfile -t modules < <(${find} -L "${dir}" -type f -not -name ".*" \ -ipath "${dir}/${module}/*" \ | cut -b${col}- \ - | sort -rV ) ) + | sort -rV ) # prepend module name modules=( "${modules[@]/#/${module}/}" ) @@ -1203,8 +1226,7 @@ find_modulefile() { fi local -A cfg=() get_module_config cfg "${dir}" "${mod}" - is_available cfg "${ReleaseStages}" "${os_release}" \ - "${HOSTNAME}" || continue + is_available cfg "${ReleaseStages}" || continue fm_modulefile="${dir}/${mod}" fm_relstage="${cfg['relstage']}" fm_dir="${dir}" @@ -1228,10 +1250,10 @@ find_modulefile() { # # avail [-hlt] [...] # -Subcommands[avail]='avail' -Options[avail]='-l help -o \?Hahlmtg: -l all -l all-release-stages -l group: ' -Options[avail]+='-l human -l long -l machine -l terse' -Help[avail]=" +Subcommands['avail']='avail' +Options['avail']='-l help -o \?Hahlmtg: -l all -l all-release-stages -l group: ' +Options['avail']+='-l human -l long -l machine -l terse' +Help['avail']=" USAGE: module avail [switches] string List all available modulefiles in the current MODULEPATH. If @@ -1275,11 +1297,12 @@ subcommand_avail() { #...................................................................... output_header() { - local caption="$1" - let i=($cols-${#caption})/2-2 - printf -- "%0.s-" $(seq 1 $i) 1>&2 + local -r caption="$1" + local -i i=0 + (( 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 -- "%0.s-" $(seq 1 "$i") 1>&2 printf -- "\n" 1>&2 } @@ -1338,7 +1361,7 @@ subcommand_avail() { local mod='' local -i max_length=1 for ((i=0; i<${#mods[@]}; i+=4)); do - if [[ ${verbosity_lvl} == 'verbose' ]]; then + if [[ ${Verbosity_lvl} == 'verbose' ]]; then local relstage=${mods[i+1]} case ${relstage} in stable ) @@ -1355,8 +1378,6 @@ subcommand_avail() { (( n > max_length )) && (( max_length=n )) available_modules+=("${mod}") done - IFS=$'\n' available_modules=($(sort --version-sort <<<"${available_modules[*]}")) - unset IFS local -i span=$(( max_length / 16 + 1 )) # compute column size local -i colsize=$(( span * 16 )) # as multiple of 16 local -i column=$cols # force a line-break @@ -1367,11 +1388,10 @@ subcommand_avail() { column=0 fi if (( column+colsize < cols )); then - fmt="%-${colsize}s" + printf "%-${colsize}s" "${mod}" 1>&2 else - fmt="%-s" + printf "%-s" "${mod}" 1>&2 fi - printf "${fmt}" "${mod}" 1>&2 column+=colsize done printf -- "\n\n" 1>&2 @@ -1386,7 +1406,7 @@ subcommand_avail() { while (($# > 0)); do case $1 in -\? | -H | --help ) - print_help "${subcommand}" + print_help "${SubCommand}" ;; -a | --all | --all-release-stages ) opt_use_relstages="${ReleaseStages}" @@ -1426,17 +1446,15 @@ subcommand_avail() { if (( ${#pattern[@]} == 0 )); then pattern+=( '' ) fi - IFS=':' - local -a modulepath=(${MODULEPATH}) - unset IFS - local dir - local group='' - local groups=() - local ol='' - local name='' + local -- dir='' + local -- group='' + local -- groups=() + local -- ol='' + local -- name='' + local -a modulepath=() + IFS=':' read -r -a modulepath <<<"${MODULEPATH}" for dir in "${modulepath[@]}"; do - find_overlay ol group "${dir}" - if (( $? == 0 )); then + if find_overlay ol group "${dir}"; then name="${group}" else name="${dir}" @@ -1449,14 +1467,14 @@ subcommand_avail() { # BASH doesn't support list as values of dictionaries. We use # reference variables to work around this. if [[ ! -v modulepath_${group} ]]; then - typeset -a modulepath_${group} + typeset -a "modulepath_${group}" fi - typeset -n path=modulepath_${group} + typeset -n path="modulepath_${group}" path+=("${dir}") # Create ordered list of groups. In the next step we # loop over this list. for group in "${groups[@]}"; do - if [[ "${group}" == ${name} ]]; then + if [[ "${group}" == "${name}" ]]; then # resume with next dir continue 2 fi @@ -1473,7 +1491,7 @@ subcommand_avail() { local ref="${group//[\/ .-]/_}" [[ -v modulepath_${ref} ]] || continue - typeset -n path=modulepath_${ref} + typeset -n path"=modulepath_${ref}" get_available_modules \ mods \ "${string}*" \ @@ -1490,9 +1508,9 @@ subcommand_avail() { # # use [-a|--append|-p|--prepend] [directory|group|release_stage...] # -Subcommands[use]='use' -Options[use]='-l help -o \?Hap -l append -l prepend' -Help[use]=" +Subcommands['use']='use' +Options['use']='-l help -o \?Hap -l append -l prepend' +Help['use']=" USAGE: module use [-a|--append|-p|--prepend] [directory|group|release_stage|...] Without arguments this sub-command displays information about @@ -1517,9 +1535,8 @@ SWITCHES: " subcommand_use() { - IFS=':' - local -a modulepath=(${MODULEPATH}) - unset IFS + local -a modulepath=() + IFS=':' read -r -a modulepath <<<"${MODULEPATH}" local add2path_func='std::append_path' #...................................................................... @@ -1530,18 +1547,18 @@ subcommand_use() { #...................................................................... print_info() { print_ol_info(){ - local only_used="$1" + local used="$1" # print used or unused overlays local ol='' for ol in "${Overlays[@]}"; do - [[ ${OverlayInfo[${ol}:used]} == ${only_used} ]] || continue - local inst_root="${OverlayInfo[${ol}:inst_root]}" - local mod_root="${OverlayInfo[${ol}:mod_root]}" + [[ ${OverlayInfo[${ol}:used]} == "${used}" ]] || continue + local install_root="${OverlayInfo[${ol}:install_root]}" + local modulefiles_root="${OverlayInfo[${ol}:modulefiles_root]}" local txt="\t${ol}" - if [[ ${inst_root} == ${mod_root} ]]; then - txt+="\n\t\t${inst_root}" + if [[ ${install_root} == "${modulefiles_root}" ]]; then + txt+="\n\t\t${install_root}" else - txt+="\n\t\t${inst_root} (install root)" - txt+="\n\t\t${mod_root} (modulefiles root)" + txt+="\n\t\t${install_root} (install root)" + txt+="\n\t\t${modulefiles_root} (modulefiles root)" fi case "${OverlayInfo[${ol}:type]}" in "${ol_hiding}" ) @@ -1590,17 +1607,16 @@ subcommand_use() { std::info '' std::info "Additonal directories in MODULEPATH:" - let n=0 + local -i i=0 + local -i n=0 local group for (( i=0; i<${#modulepath[@]}; i++)); do if ! find_overlay ol group "${modulepath[i]}"; then std::info "\t${modulepath[i]}" - let n+=1 + (( n+=1 )) fi done - if (( n == 0 )); then - std::info "\tnone" - fi + (( n == 0 )) && std::info "\tnone" std::info "" } # print_info @@ -1622,21 +1638,21 @@ subcommand_use() { # other overlays in these groups for group in ${UsedGroups//:/ }; do # is this group in the to be added overlay? - local dir="${OverlayInfo[${ol_name}:mod_root]}/" + local dir="${OverlayInfo[${ol_name}:modulefiles_root]}/" dir+="${group}/${PMODULES_MODULEFILES_DIR}" [[ -d "${dir}" ]] || continue # no dir="/${group}/${PMODULES_MODULEFILES_DIR}" local -a dirs=() for ol in "${UsedOverlays[@]}"; do - dirs+=( ${OverlayInfo[${ol}:mod_root]}${dir} ) + dirs+=( "${OverlayInfo[${ol}:modulefiles_root]}${dir}" ) done std::remove_path MODULEPATH "${dirs[@]}" done fi scan_groups "${ol_name}" for group in ${UsedGroups//:/ }; do - local dir="${OverlayInfo[${ol_name}:mod_root]}/" + local dir="${OverlayInfo[${ol_name}:modulefiles_root]}/" dir+="${group}/${PMODULES_MODULEFILES_DIR}" if [[ -d "${dir}" ]]; then std::prepend_path MODULEPATH "${dir}" @@ -1654,7 +1670,7 @@ subcommand_use() { done export_env UsedOverlays - g_env_must_be_saved='yes' + EnvMustBeSaved='yes' scan_groups "${UsedOverlays[@]}" } @@ -1667,7 +1683,7 @@ subcommand_use() { std::append_path UsedGroups "$1" local ol_name for ol_name in "${UsedOverlays[@]}"; do - local dir="${OverlayInfo[${ol_name}:mod_root]}/$1/${PMODULES_MODULEFILES_DIR}" + local dir="${OverlayInfo[${ol_name}:modulefiles_root]}/$1/${PMODULES_MODULEFILES_DIR}" [[ -d "${dir}" ]] || continue std::prepend_path MODULEPATH "${dir}" @@ -1676,31 +1692,32 @@ subcommand_use() { } #.............................................................. - local arg=$1 - + local -- arg="$1" + local -i rc=0 if is_release_stage "${arg}"; then # argument is release stage - std::append_path UsedReleaseStages "${arg}" - return $? + std::append_path UsedReleaseStages "${arg}" || rc=$? + return ${rc} fi if [[ -v OverlayInfo[${arg}:type] ]]; then - use_overlay "${arg}" - return $? + use_overlay "${arg}" || rc=$? + return ${rc} fi if [[ ! -v GroupDepths[${arg}] ]]; then # this scan is required if a new group has been # create inside an used overlay scan_groups "${UsedOverlays[@]}" - g_env_must_be_saved='yes' + EnvMustBeSaved='yes' fi if [[ -n ${GroupDepths[${arg}]} ]]; then - use_group "${arg}" - return $? + use_group "${arg}" || rc=$? + return ${rc} fi if [[ -d ${arg} ]]; then - local dir=$(cd "${arg}" && pwd -L) - ${add2path_func} MODULEPATH "${dir}" - return $? + local dir='' + dir=$(std::get_abspath "${arg}") + ${add2path_func} MODULEPATH "${dir}" || rc=$? + return ${rc} fi die_invalid_value "use flag, group, overlay or directory" \ "${arg}" @@ -1711,7 +1728,7 @@ subcommand_use() { while (( $# > 0)); do case "$1" in -\? | -H | --help ) - print_help "${subcommand}" + print_help "${SubCommand}" ;; -a | --append ) add2path_func='std::append_path' @@ -1738,7 +1755,7 @@ subcommand_use() { for arg in "${args[@]}"; do use "${arg}" done - g_env_must_be_saved='yes' + EnvMustBeSaved='yes' export_env 'MODULEPATH' } @@ -1746,18 +1763,17 @@ subcommand_use() { # # unuse directory|group|release_stage|... # -Subcommands[unuse]='unuse' -Options[unuse]='-o \?H -l help' -Help[unuse]=' +Subcommands['unuse']='unuse' +Options['unuse']='-o \?H -l help' +Help['unuse']=' unuse directory|group|release... Remove the given modulefiles directory, group, release stage, flag from the search path. ' subcommand_unuse() { - IFS=':' - local -a modulepath=(${MODULEPATH}) - unset IFS + local -a modulepath=() + IFS=':' read -r -a modulepath <<<"${MODULEPATH}" #...................................................................... unuse() { @@ -1768,26 +1784,26 @@ subcommand_unuse() { if [[ -n "${LOADEDMODULES}" ]] && \ [[ "${LOADEDMODULES}" != Pmodules/+([.0-9rc]) ]]; then std::die 3 "%s %s: %s %s" \ - "${CMD}" "${subcommand}" \ + "${CMD}" "${SubCommand}" \ "overlay cannot be removed since" \ "some modules are still loaded!" fi - [[ "${OverlayInfo[ol_name]:mod_root}" == "${PMODULES_HOME%%/Tools*}" ]] && \ + [[ "${OverlayInfo[ol_name]:modulefiles_root}" == "${PMODULES_HOME%%/Tools*}" ]] && \ std::die 3 "%s %s: %s -- %s" \ - "${CMD}" "${subcommand}" \ + "${CMD}" "${SubCommand}" \ "cannot remove base overlay" \ "${ol_name}" [[ ${OverlayInfo[${ol_name}:used]} != 'yes' ]] && \ std::die 3 "%s %s: %s -- %s" \ - "${CMD}" "${subcommand}" \ + "${CMD}" "${SubCommand}" \ "not an used overlay" \ "${ol_name}" # make sure first index is '0' (it should, but you never know) UsedOverlays=( "${UsedOverlays[@]}" ) [[ "${ol_name}" != "${UsedOverlays[0]}" ]] && \ std::die 3 "%s %s: %s %s -- %s" \ - "${CMD}" "${subcommand}" \ + "${CMD}" "${SubCommand}" \ "overlay cannot be removed since" \ "it not on top of the stack" \ "${ol_name}" @@ -1795,10 +1811,10 @@ subcommand_unuse() { if [[ "${OverlayInfo[${ol_name}:type]}" == "${ol_replacing}" ]]; then # if this overlay hides groups, we have to re-add # the modules made available by other overlays - local mod_root=${OverlayInfo[${ol_name}:mod_root]} + local modulefiles_root=${OverlayInfo[${ol_name}:modulefiles_root]} for group in ${UsedGroups//:/ }; do # is this group in the to be removed overlay? - local dir="${mod_root}/${group}/${PMODULES_MODULEFILES_DIR}" + local dir="${modulefiles_root}/${group}/${PMODULES_MODULEFILES_DIR}" [[ -d "${dir}" ]] || continue # no dir="/${group}/${PMODULES_MODULEFILES_DIR}" @@ -1817,18 +1833,18 @@ subcommand_unuse() { OverlayExcludes=() local ol local item - for ol in "${UsedOverlays}"; do + for ol in "${UsedOverlays[@]}"; do IFS=':' read -r -a excludes <<< "${OverlayInfo[${ol}:excludes]}" for item in "${excludes[@]}"; do OverlayExcludes[${item}]=1 done done - g_env_must_be_saved='yes' + EnvMustBeSaved='yes' export_env UsedOverlays local dir for dir in "${modulepath[@]}"; do - [[ "${dir}" =~ "${OverlayInfo[${ol_name}:mod_root]}" ]] && \ + [[ "${dir}" == "${OverlayInfo[${ol_name}:modulefiles_root]}" ]] && \ std::remove_path MODULEPATH "${dir}" done } @@ -1838,7 +1854,7 @@ subcommand_unuse() { if (( ${GroupDepths[${arg}]} > 0 )); then # argument is a hierarchical group in our root std::die 3 "%s %s: %s -- %s" \ - "${CMD}" "${subcommand}" \ + "${CMD}" "${SubCommand}" \ "illegal group" \ "${arg}" fi @@ -1846,7 +1862,7 @@ subcommand_unuse() { local var="PMODULES_LOADED_${arg^^}" if [[ -n "${!var}" ]]; then std::die 3 "%s %s: %s -- %s" \ - "${CMD}" "${subcommand}" \ + "${CMD}" "${SubCommand}" \ "cannot remove group due to loaded modules" \ "${arg}" fi @@ -1871,7 +1887,8 @@ subcommand_unuse() { return 0 fi if [[ -d ${arg} ]]; then - local dir=$(std::get_abspath "${arg}") + local dir='' + dir=$(std::get_abspath "${arg}") std::remove_path MODULEPATH "${dir}" return 0 fi @@ -1881,7 +1898,7 @@ subcommand_unuse() { fi std::die 3 "%s %s: %s -- %s" \ - "${CMD}" "${subcommand}" \ + "${CMD}" "${SubCommand}" \ "invalid keyword, group, overlay or directory" \ "${arg}" } # unuse() @@ -1891,7 +1908,7 @@ subcommand_unuse() { while (( $# > 0)); do case "$1" in -\? | -H | --help ) - print_help "${subcommand}" + print_help "${SubCommand}" ;; -- ) shift 1 @@ -1906,13 +1923,13 @@ subcommand_unuse() { done if (( ${#args[@]} == 0 )); then std::die 3 "%s %s: %s" \ - "${CMD}" "${subcommand}" \ + "${CMD}" "${SubCommand}" \ 'missing argument' fi for arg in "${args[@]}"; do unuse "${args[@]}" done - g_env_must_be_saved='yes' + EnvMustBeSaved='yes' export_env 'MODULEPATH' } @@ -1924,9 +1941,9 @@ subcommand_unuse() { # either compile Modules with --enable-beginenv or remove the # sub-command # -Subcommands[update]='update' -Options[update]='-o \?H -l help' -Help[update]=' +Subcommands['update']='update' +Options['update']='-o \?H -l help' +Help['update']=' USAGE: module update Attempt to reload all loaded modulefiles. @@ -1940,7 +1957,7 @@ subcommand_update() { # # refresh # -Subcommands[refresh]='refresh' +Subcommands['refresh']='refresh' Options[refresh]='-o \?H -l help' Help[refresh]=' USAGE: @@ -1967,7 +1984,7 @@ init_modulepath() { local ol for ol in "${UsedOverlays[@]}"; do for group in ${UsedGroups//:/ }; do - local dir="${OverlayInfo[${ol}:mod_root]}/${group}/${PMODULES_MODULEFILES_DIR}" + local dir="${OverlayInfo[${ol}:modulefiles_root]}/${group}/${PMODULES_MODULEFILES_DIR}" if [[ -d "${dir}" ]]; then std::prepend_path MODULEPATH "${dir}" fi @@ -1982,14 +1999,14 @@ pmodules_init() { for group in ${DefaultGroups//:/ }; do std::append_path UsedGroups "${group}" done - g_env_must_be_saved='yes' + EnvMustBeSaved='yes' } init_used_releases() { declare -g UsedReleaseStages='' for r in ${DefaultReleaseStages//:/ }; do std::append_path UsedReleaseStages "${r}" done - g_env_must_be_saved='yes' + EnvMustBeSaved='yes' } init_overlay_vars() { declare -ag UsedOverlays=( 'base' ) @@ -1997,19 +2014,26 @@ pmodules_init() { } init_manpath() { - std::replace_path MANPATH "${PMODULES_HOME%/*}/.*" + : " + Initialize MANPATH. Use systen wide configuration file + if exist or set some defaults. + + " + std::remove_path MANPATH "${PMODULES_HOME%/*}/.*" if [[ -r /etc/man.config ]]; then declare _manconf='/etc/man.config' elif [[ -r /etc/man.conf ]]; then declare _manconf='/etc/man.conf' fi - if [[ -n ${_manconf} ]]; then - while read name value rest; do + if [[ -v _manconf ]]; then + # initialize from system configuration file + while read -r name value rest; do std::append_path MANPATH "${value}" done < <(grep "^MANPATH\s" "${_manconf}") unset _manconf else + # set defaults std::append_path MANPATH "${PMODULES_HOME}/share/man" std::append_path MANPATH "/usr/share/man" fi @@ -2041,9 +2065,9 @@ pmodules_init() { # # purge # -Subcommands[purge]='purge' -Options[purge]='-o \?H -l help' -Help[purge]=' +Subcommands['purge']='purge' +Options['purge']='-o \?H -l help' +Help['purge']=' USAGE: module purge Unload all loaded modulefiles. @@ -2061,7 +2085,7 @@ subcommand_purge() { while (( $# > 0)); do case "$1" in -\? | -H | --help ) - print_help "${subcommand}" + print_help "${SubCommand}" ;; -- ) shift 1 @@ -2092,9 +2116,9 @@ subcommand_purge() { # # list [-hlt] # -Subcommands[list]='list' -Options[list]='-l help -o \?Hhlt -l human -l long -l terse' -Help[list]=' +Subcommands['list']='list' +Options['list']='-l help -o \?Hhlt -l human -l long -l terse' +Help['list']=' USAGE: module list List loaded modules. @@ -2106,17 +2130,17 @@ subcommand_list() { # get list of loaded modules with stripped MODULEPATH IFS=':' read -r -a modules \ < <( ${sed} "s;${MODULEPATH//:/\/\\\|}/;;g" <<< "${_LMFILES_}" ) - local strs=() - local -i n=1 + local -- strs=() + local -i n=1 # enumeration of loaded modules local -i colsize=0 + local -- s='' for module in "${modules[@]%.lua}"; do - local s=$(printf "%2d) %-s" $n "${module}") + s=$(printf "%2d) %-s" "$n" "${module}") strs+=( "$s" ) - local -i sizeof_s=${#s} - (( sizeof_s > colsize )) && colsize=sizeof_s - n+=1 + (( ${#s} > colsize )) && (( colsize=${#s} )) + (( n+=1 )) done - colsize+=2 + (( colsize+=2 )) local -i cols=80 [[ -t 1 && -t 2 ]] && cols=$(${tput} cols) @@ -2133,7 +2157,7 @@ subcommand_list() { else printf "%-s" "$s" 1>&2 # last column fi - column+=colsize + (( column+=colsize )) done printf -- "\n\n" 1>&2 } @@ -2161,7 +2185,7 @@ subcommand_list() { < <( ${sed} "s;${MODULEPATH//:/\/\\\|}/;;g" <<< "${_LMFILES_}" ) printf "Currently Loaded Modules:\n" 1>&2 for module in "${modules[@]%.lua}"; do - printf "${module}\n" 1>&2 + printf "%s\n" "${module}" 1>&2 done printf -- "\n\n" 1>&2 } @@ -2171,7 +2195,7 @@ subcommand_list() { while (( $# > 0 )); do case $1 in -\? | -H | --help ) - print_help "${subcommand}" + print_help "${SubCommand}" ;; -h | --human ) output_function='human_readable_output' @@ -2198,7 +2222,7 @@ subcommand_list() { done if (( ${#args[@]} > 0 )); then std::die 3 "%s %s: %s" \ - "${CMD}" "${subcommand}" \ + "${CMD}" "${SubCommand}" \ "no arguments allowed" fi @@ -2209,9 +2233,9 @@ subcommand_list() { # # clear # -Subcommands[clear]='clear' -Options[clear]='-o \?H -l help' -Help[clear]=' +Subcommands['clear']='clear' +Options['clear']='-o \?H -l help' +Help['clear']=' USAGE: module clear Force the Modules package to believe that no modules are @@ -2223,7 +2247,7 @@ subcommand_clear() { while (( $# > 0 )); do case $1 in -\? | -H | --help ) - print_help "${subcommand}" + print_help "${SubCommand}" ;; -- ) shift 1 @@ -2238,7 +2262,7 @@ subcommand_clear() { done if (( ${#args[@]} > 0 )); then std::die 3 "%s %s: %s" \ - "${CMD}" "${subcommand}" \ + "${CMD}" "${SubCommand}" \ "no arguments allowed" fi pmodules_init @@ -2248,16 +2272,16 @@ subcommand_clear() { # # search [switches] [STRING...] # -Subcommands[search]='search' -Subcommands[find]='search' -Options[search]='-o a\?H -l help -l no-header -l print-modulefiles ' -Options[search]+='-l release-stage: -l with: -l all-release-stages -l src: -l print-csv ' -Options[search]+='-l verbose ' -Options[search]+='-l all-deps -l wrap ' -Options[search]+='-l glob ' -Options[search]+='-l newest ' -Options[search]+='-l group:' -Help[search]=' +Subcommands['search']='search' +Subcommands['find']='search' +Options['search']='-o a\?H -l help -l no-header -l print-modulefiles ' +Options['search']+='-l release-stage: -l with: -l all-release-stages -l src: -l print-csv ' +Options['search']+='-l verbose ' +Options['search']+='-l all-deps -l wrap ' +Options['search']+='-l glob ' +Options['search']+='-l newest ' +Options['search']+='-l group:' +Help['search']=' USAGE: module find|search [switches] STRING... Search installed modules. If an argument is given, search @@ -2309,7 +2333,7 @@ subcommand_search() { local opt_print_modulefiles='no' local opt_print_csv='no' local opt_print_verbose='no' - local opt_use_releases=':' + local opt_use_relstages=':' local opt_all_deps='no' local opt_wrap='no' local opt_glob='no' @@ -2344,7 +2368,7 @@ subcommand_search() { print_header_default() { std::info '' std::info "${fmt}" "Module" "Rel.stage" "Group" "Overlay" "Requires" - std::info '-%.0s' $(seq 1 ${cols}) + std::info '-%.0s' $(seq 1 "${cols}") } print_line_default() { @@ -2355,13 +2379,14 @@ subcommand_search() { fi std::info "${str}" } + local -a deps=( "${@:6}" ) + (( ${#deps[@]} == 0 )) && deps[0]='' + local -- str='' if [[ "${opt_wrap}" == 'no' ]]; then - local deps="${@:6}" - local str=$(printf "${fmt}" "$1" "$2" "$3" "$5" "${deps[@]}") + str=$(printf "${fmt}" "$1" "$2" "$3" "$5" "${deps[*]}") write_line "${str}" else - local deps=( "${@:6}" ) - local str=$(printf "${fmt}" "$1" "$2" "$3" "$5" "${deps[0]}") + str=$(printf "${fmt}" "$1" "$2" "$3" "$5" "${deps[0]}") for (( i = 1; i < ${#deps[@]}; i++ )); do if (( ${#str} + ${#deps[i]} + 1 <= cols )); then str+=" ${deps[i]}" @@ -2385,7 +2410,7 @@ subcommand_search() { } print_line_verbose() { - local deps="${@:6}" + local deps="${*:6}" [[ -z ${deps} ]] && deps="(none)" std::info "$1:" std::info " release stage: $2" @@ -2437,9 +2462,10 @@ subcommand_search() { _script='{} END{print}' fi ${func_print_header} - while read -a toks; do + local -a toks=() + while read -r -a toks; do ${func_print_line} "${toks[@]}" - done < <("${sort}" --version-sort -k 1,1 -k 6,6 -k 7,7 "${tmpfile}" | \ + done < <("${sort}" --version-sort -k 1,1 -k 6,6 -k 7,7 "${TmpFile}" | \ ${awk} "${with_modules} ${_script}") } @@ -2457,16 +2483,16 @@ subcommand_search() { local -r group="$2" shift 2 local -a modulepath=("$@") - + local -i depth=0 if (( ${#modulepath[@]} == 0 )); then # loop over all directories which can be added to # MODULEPATH inside current group - local depth=${GroupDepths[${group}]} + depth=${GroupDepths[${group}]} local s='' if (( depth > 0 )); then s=$(printf '/*%.0s' $(seq 1 ${depth})) fi - modulepath=( ${src_prefix[@]/%//${group}/modulefiles$s} ) + modulepath=( "${src_prefix[@]/%//${group}/modulefiles$s}" ) local -- dir='' for dir in "${modulepath[@]}"; do searched_moduledirs[${dir}]=1 @@ -2475,7 +2501,7 @@ subcommand_search() { # get and print all available modules in $mpath # with respect to the requested release stage - # tmpfile: module/version relstage group dependencies... + # TmpFile: module/version relstage group dependencies... local mods get_available_modules \ mods \ @@ -2499,7 +2525,7 @@ subcommand_search() { get_module_prefix prefix "${modulefile}" local dependencies_file="${prefix}/.dependencies" if [[ -n ${prefix} ]] && [[ -r "${dependencies_file}" ]]; then - deps=($(< "${dependencies_file}")) + mapfile -t deps < "${dependencies_file}" else deps=() fi @@ -2507,24 +2533,25 @@ subcommand_search() { # get dependencies encoded in directory name local deps=() local -i j - IFS='/' # note: IFS is used to concat in the for loop! - local toks=( ${modulefile} ) + local -a toks + IFS='/' + read -r -a toks <<< "${modulefile}" for ((j = -depth-2; j < -2; j += 2)); do deps+=( "${toks[*]: $j:2}" ); done unset IFS fi - echo ${name} ${relstage} ${group} ${modulefile} \ - ${ol} \ - ${deps[@]} >> "${tmpfile}" + echo "${name}" "${relstage}" "${group}" "${modulefile}" \ + "${ol}" \ + "${deps[@]}" >> "${TmpFile}" done } while (( $# > 0 )); do case $1 in -\? | -H | --help ) - print_help "${subcommand}" + print_help "${SubCommand}" ;; --all-deps ) opt_all_deps='yes' @@ -2561,7 +2588,7 @@ subcommand_search() { else local arg=${1/--with=} fi - if [[ -z ${arg} ]] || [[ "${arg}" =~ "-*" ]]; then + if [[ -z ${arg} ]] || [[ "${arg}" == -* ]]; then std::die 1 "%s %s: %s -- %s" \ "${CMD}" 'search' \ "illegal value for --with option" \ @@ -2595,7 +2622,7 @@ subcommand_search() { "illegal value for --src option" \ "${dir} is not a directory" fi - src_prefix+=( $(std::get_abspath "${src_prefix}") ) + src_prefix+=( "$(std::get_abspath "${src_prefix}")" ) ;; -v | --verbose ) opt_print_verbose='yes' @@ -2611,9 +2638,9 @@ subcommand_search() { ;; --group | --group=* ) if [[ $1 == *=* ]]; then - groups+=( ${1/--*=} ) + groups+=( "${1/--*=}" ) else - groups+=( $2 ) + groups+=( "$2" ) shift fi ;; @@ -2632,7 +2659,7 @@ subcommand_search() { if (( ${#src_prefix[@]} == 0 )); then local ol='' for ol in "${UsedOverlays[@]}"; do - src_prefix+=( "${OverlayInfo[${ol}:mod_root]}" ) + src_prefix+=( "${OverlayInfo[${ol}:modulefiles_root]}" ) done fi @@ -2646,9 +2673,9 @@ subcommand_search() { # :FIXME: do we need this? if (( ${#GroupDepths[@]} == 0 )) || \ - [[ ${src_prefix} != ${PMODULES_HOME%%/Tools*} ]]; then + [[ ${src_prefix} != "${PMODULES_HOME%%/Tools*}" ]]; then scan_groups "${src_prefix}" - g_env_must_be_saved='yes' + EnvMustBeSaved='yes' fi if (( ${#groups[@]} == 0 )); then @@ -2671,7 +2698,7 @@ subcommand_search() { for dir in "${modulepath[@]}"; do # skip already searched directories [[ -v searched_moduledirs[${dir}] ]] && continue - dirs+=(${dir}) + dirs+=( "${dir}" ) done if (( ${#dirs[@]} > 0 )); then for module in "${modules[@]}"; do @@ -2686,9 +2713,9 @@ subcommand_search() { # # help [module|sub-command] # -Subcommands[help]='help' -Options[help]='-o hHV\? -l version -l help' -Help[help]=' +Subcommands['help']='help' +Options['help']='-o hHV\? -l version -l help' +Help['help']=' USAGE: module [ switches ] [ subcommand ] [subcommand-args ] @@ -2735,7 +2762,7 @@ subcommand_help() { while (( $# > 0 )); do case $1 in -\? | -h | -H | --help ) - print_help "${subcommand}" + print_help "${SubCommand}" ;; -V | --version ) print_help 'version' @@ -2761,7 +2788,7 @@ subcommand_help() { else # :FIXME: print help of newest *available* module # (respecting UsedReleaseStages) - "${modulecmd}" "${Shell}" "${subcommand}" "${arg}" + "${modulecmd}" "${Shell}" "${SubCommand}" "${arg}" fi done } @@ -2770,14 +2797,14 @@ subcommand_help() { # # whatis # -Subcommands[whatis]='whatis' -Options[whatis]='-o \?Ha -l help -l all' -Help[whatis]=' +Subcommands['whatis']='whatis' +Options['whatis']='-o \?Ha -l help -l all' +Help['whatis']=' USAGE: module whatis [modulefile...] Display the information set up by the module-whatis commands inside the specified modulefile(s). If no modulefile is - specified, all 'whatis' lines will be shown. + specified, all whatis lines will be shown. ' subcommand_whatis() { @@ -2786,7 +2813,7 @@ subcommand_whatis() { while (( $# > 0 )); do case $1 in -\? | --help ) - print_help "${subcommand}" + print_help "${SubCommand}" ;; -a | --all ) options+=( '-a' ) @@ -2807,9 +2834,10 @@ subcommand_whatis() { for group in "${!GroupDepths[@]}"; do local mod_name='' local file_name='' - while read mod_name file_name; do + while read -r mod_name file_name; do [[ -n ${file_name} ]] || continue - local whatis=$("${modulecmd}" bash \ + local whatis='' + whatis=$("${modulecmd}" bash \ whatis \ "${file_name}" \ 2>&1 1>/dev/null) @@ -2827,13 +2855,13 @@ subcommand_whatis() { # # apropos # -Subcommands[apropos]='apropos' -Subcommands[keyword]='apropos' -Options[apropos]='-o \?Ha -l help -l all' -Help[apropos]=' +Subcommands['apropos']='apropos' +Subcommands['keyword']='apropos' +Options['apropos']='-o \?Ha -l help -l all' +Help['apropos']=' USAGE: module apropos string - module keyword string Seeks through the 'whatis' informations of + 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. ' @@ -2844,7 +2872,7 @@ subcommand_apropos() { while (( $# > 0 )); do case $1 in -\? | --help ) - print_help "${subcommand}" + print_help "${SubCommand}" ;; -a | --all ) options+=( '-a' ) @@ -2862,11 +2890,11 @@ subcommand_apropos() { done if (( ${#args[@]} == 0 )); then std::die 3 "%s %s: %s" \ - "${CMD}" "${subcommand}" \ + "${CMD}" "${SubCommand}" \ "no search string specified" elif (( ${#args[@]} > 1 )); then std::die 3 "%s %s: %s" \ - "${CMD}" "${subcommand}" \ + "${CMD}" "${SubCommand}" \ "more then one search string specified" fi local arg="${args[0]}" @@ -2874,9 +2902,10 @@ subcommand_apropos() { for group in "${!GroupDepths[@]}"; do local mod_name='' local file_name='' - while read mod_name file_name; do + while read -r mod_name file_name; do [[ -n ${file_name} ]] || continue - local whatis=$("${modulecmd}" bash \ + local whatis='' + whatis=$("${modulecmd}" bash \ whatis \ "${file_name}" \ 2>&1 1>/dev/null) @@ -2907,9 +2936,9 @@ subcommand_apropos() { # # save [collection] # -Subcommands[save]='save' -Options[save]='-o \?H -l help' -Help[save]=" +Subcommands['save']='save' +Options['save']='-o \?H -l help' +Help['save']=" USAGE: module save [collection] Record the currently set MODULEPATH directory list and the @@ -2928,7 +2957,7 @@ subcommand_save() { while (( $# > 0 )); do case $1 in -\? | -H | --help ) - print_help "${subcommand}" + print_help "${SubCommand}" ;; --system ) opt_system='yes' @@ -2969,15 +2998,15 @@ subcommand_save() { local -a tmp=() local -- grp='' - IFS=':' read -a tmp <<< "${UsedReleaseStages}" + IFS=':' read -r -a tmp <<< "${UsedReleaseStages}" items+=( "${tmp[@]}") - IFS=':' read -a tmp <<< "${UsedGroups}" + IFS=':' read -r -a tmp <<< "${UsedGroups}" for grp in "${tmp[@]}"; do # skip hierarchical groups (( ${GroupDepths[${tmp}]} > 0 )) && continue items+=( "${grp}") done - IFS=':' read -a tmp <<< "${UsedOverlays}" + IFS=':' read -r -a tmp <<< "${UsedOverlays}" items+=( "${tmp[@]}") for item in "${items[@]}"; do [[ "${item}" == 'base' ]] && continue @@ -2986,7 +3015,7 @@ subcommand_save() { # save additional module directories local -a modulepath=() - IFS=':' read -a modulepath <<< "${MODULEPATH}" + IFS=':' read -r -a modulepath <<< "${MODULEPATH}" local dir='' local ol='' local grp='' @@ -2997,7 +3026,7 @@ subcommand_save() { # save loaded modules local -a modules=() - IFS=':' read -a modules <<< "${LOADEDMODULES}" + IFS=':' read -r -a modules <<< "${LOADEDMODULES}" local -- m='' for m in "${modules[@]}"; do [[ $m == Pmodules/* ]] && continue @@ -3013,9 +3042,9 @@ subcommand_save() { # # restore [collection] # -Subcommands[restore]='restore' -Options[restore]='-o \?H -l help' -Help[restore]=" +Subcommands['restore']='restore' +Options['restore']='-o \?H -l help' +Help['restore']=" USAGE: module restore [collection] Restore the environment state as defined in collection. @@ -3033,8 +3062,8 @@ search_collection(){ fi local -- _ol for _ol in "${UsedOverlays[@]}"; do - if [[ -r "${OverlayInfo[${_ol}:inst_root]}/collections/${_collection}" ]]; then - _path="${OverlayInfo[${_ol}:inst_root]}/collections" + if [[ -r "${OverlayInfo[${_ol}:install_root]}/collections/${_collection}" ]]; then + _path="${OverlayInfo[${_ol}:install_root]}/collections" return 0 fi done @@ -3046,7 +3075,7 @@ subcommand_restore() { while (( $# > 0 )); do case $1 in -\? | -H | --help ) - print_help "${subcommand}" + print_help "${SubCommand}" ;; -- ) shift 1 @@ -3071,7 +3100,7 @@ subcommand_restore() { done # reset/unload everything (except Pmodules/) - IFS=':' read -a modules <<< "${LOADEDMODULES}" + IFS=':' read -r -a modules <<< "${LOADEDMODULES}" local -- m='' for ((i=${#modules[@]}-1; i>=0; i--)); do [[ ${modules[$i]} == Pmodules/* ]] && continue @@ -3081,20 +3110,20 @@ subcommand_restore() { local -a items=() local -a tmp=() local -- item='' - local -a grps=() + local -a default_grps=() - IFS=':' read -a tmp <<< "${UsedReleaseStages}" + IFS=':' read -r -a tmp <<< "${UsedReleaseStages}" items+=( "${tmp[@]}") # remove all groups with the exception of the default groups - IFS=':' read -a tmp <<< "${UsedGroups}" - IFS=':' read -a default_grps <<< "${DefaultGroups}" + IFS=':' read -r -a tmp <<< "${UsedGroups}" + IFS=':' read -r -a default_grps <<< "${DefaultGroups}" for item in "${tmp[@]}"; do (( ${GroupDepths["${item}"]} > 0 )) && continue std::is_member_of_array "${item}" default_grps && continue items+=( "${grp}" ) done - IFS=':' read -a tmp <<< "${UsedOverlays}" + IFS=':' read -r -a tmp <<< "${UsedOverlays}" for item in "${tmp[@]}"; do [[ "${item}" == 'base' ]] && continue items+=( "${tmp[@]}") @@ -3110,19 +3139,19 @@ subcommand_restore() { # load collection for collection in "${collections[@]}"; do - cat "${collection}" + ${cat} "${collection}" done - g_env_must_be_saved='no' + EnvMustBeSaved='no' } ############################################################################## # # module savelist [pattern...] # -Subcommands[savelist]='savelist' -Options[savelist]='-o \?H -l help' -Help[savelist]=" +Subcommands['savelist']='savelist' +Options['savelist']='-o \?H -l help' +Help['savelist']=" USAGE: module savelist [pattern...] List collections that are currently saved under the user's @@ -3144,7 +3173,7 @@ subcommand_savelist() { local _pattern local _coll for _pattern in "$@"; do - while read _coll; do + while read -r _coll; do _result+=( "${_coll}" ) done < <(find "${gc_dirs[@]}" -type f -ipath "*/${_pattern}" -printf "%P\n" 2>/dev/null) done @@ -3169,7 +3198,7 @@ subcommand_savelist() { while (( $# > 0 )); do case $1 in -\? | -H | --help ) - print_help "${subcommand}" + print_help "${SubCommand}" ;; -- ) shift 1 @@ -3190,7 +3219,7 @@ subcommand_savelist() { _dirs=() local _ol for _ol in "${UsedOverlays[@]}"; do - _dirs+=( "${OverlayInfo[${_ol}:inst_root]}/collections" ) + _dirs+=( "${OverlayInfo[${_ol}:install_root]}/collections" ) done print_collections "\nSystem collections:" _dirs "${args[@]}" } @@ -3199,9 +3228,9 @@ subcommand_savelist() { # # module saverm [collection...] # -Subcommands[saverm]='saverm' -Options[saverm]='-o \?H -l help' -Help[saverm]=" +Subcommands['saverm']='saverm' +Options['saverm']='-o \?H -l help' +Help['saverm']=" USAGE: module saverm [collection] Delete the collection file under the user's collection @@ -3213,7 +3242,7 @@ subcommand_saverm() { while (( $# > 0 )); do case $1 in -\? | -H | --help ) - print_help "${subcommand}" + print_help "${SubCommand}" ;; -- ) shift 1 @@ -3246,9 +3275,9 @@ subcommand_saverm() { # # module saveshow [collection] # -Subcommands[saveshow]='saveshow' -Options[saveshow]='-o \?H -l help' -Help[saveshow]=" +Subcommands['saveshow']='saveshow' +Options['saveshow']='-o \?H -l help' +Help['saveshow']=" USAGE: module saveshow [collection...] Display the content of collection. If collection name is not @@ -3260,7 +3289,7 @@ subcommand_saveshow() { while (( $# > 0 )); do case $1 in -\? | -H | --help ) - print_help "${subcommand}" + print_help "${SubCommand}" ;; -- ) shift 1 @@ -3291,9 +3320,9 @@ subcommand_saveshow() { # # initadd module... # -Subcommands[initadd]='initadd' -Options[initadd]='-o \?H -l help' -Help[initadd]=" +Subcommands['initadd']='initadd' +Options['initadd']='-o \?H -l help' +Help['initadd']=" USAGE: module initadd modulefile... Add modulefile(s) to the shell's initialization file in the @@ -3327,9 +3356,9 @@ subcommand_initadd() { # # initprepend module... # -Subcommands[initprepend]='initprepend' -Options[initprepend]='-o \?H -l help' -Help[initprepend]=" +Subcommands['initprepend']='initprepend' +Options['initprepend']='-o \?H -l help' +Help['initprepend']=" USAGE: module initprepend modulefile... Does the same as initadd but prepends the given modules to @@ -3344,9 +3373,9 @@ subcommand_initprepend() { # # initrm module... # -Subcommands[initrm]='initrm' -Options[initrm]='-o \?H -l help' -Help[initrm]=" +Subcommands['initrm']='initrm' +Options['initrm']='-o \?H -l help' +Help['initrm']=" USAGE: module initrm modulefile... Remove modulefile(s) from the shell's initialization files. @@ -3360,9 +3389,9 @@ subcommand_initrm() { # # initswitch module1 module2 # -Subcommands[initswitch]='initswitch' -Options[initswitch]='-o \?H -l help' -Help[initswitch]=" +Subcommands['initswitch']='initswitch' +Options['initswitch']='-o \?H -l help' +Help['initswitch']=" USAGE: module initswitch modulefile1 modulefile2 Switch modulefile1 with modulefile2 in the shell's @@ -3374,7 +3403,7 @@ subcommand_initswitch() { while (( $# > 0 )); do case $1 in -\? | --help ) - print_help "${subcommand}" + print_help "${SubCommand}" ;; -- ) shift 1 @@ -3389,19 +3418,19 @@ subcommand_initswitch() { done if (( ${#args[@]} != 2 )); then std::die 3 "%s %s: %s" \ - "${CMD}" "${subcommand}" \ + "${CMD}" "${SubCommand}" \ "two arguments required not less not more" fi - "${modulecmd}" "${Shell}" "${subcommand}" "${args[@]}" + "${modulecmd}" "${Shell}" "${SubCommand}" "${args[@]}" } ############################################################################## # # initlist # -Subcommands[initlist]='initlist' -Options[initlist]='-o \?H -l help' -Help[initlist]=" +Subcommands['initlist']='initlist' +Options['initlist']='-o \?H -l help' +Help['initlist']=" USAGE: module initlist List all of the modulefiles loaded from the shell's @@ -3416,9 +3445,9 @@ subcommand_initlist() { # # initclear # -Subcommands[initclear]='initclear' -Options[initclear]='-o \?H -l help' -Help[initclear]=" +Subcommands['initclear']='initclear' +Options['initclear']='-o \?H -l help' +Help['initclear']=" USAGE: module initclear Clear all of the modulefiles from the shell's @@ -3482,7 +3511,7 @@ while (( $# > 0 )); do opts+=( "$1" ) ;; * ) - subcommand="$1" + SubCommand="$1" shift break ;; @@ -3490,17 +3519,16 @@ while (( $# > 0 )); do shift done -if [[ -z "${subcommand}" ]]; then +if [[ -z "${SubCommand}" ]]; then std::die 1 "${CMD}: no sub-command specified." fi -if [[ ! -v Subcommands[${subcommand}] ]]; then - std::die 1 "${CMD}: unknown sub-command -- ${subcommand}" +if [[ ! -v Subcommands[${SubCommand}] ]]; then + std::die 1 "${CMD}: unknown sub-command -- ${SubCommand}" fi # restore variables from last call -unset Version -if [[ -n ${PMODULES_ENV} ]]; then +if [[ -v PMODULES_ENV ]]; then eval "$("${base64}" -d <<< "${PMODULES_ENV}" 2>/dev/null)" fi # Version should now be defined again, if not: @@ -3511,7 +3539,10 @@ fi # We (re-)initialise the Pmodules system, if # - PMODULES_ENV was not set/is empty # - A new Pmodules version has been loaded -if [[ -z ${Version} ]] || [[ ${Version} != ${PMODULES_VERSION} ]]; then +if [[ ! -v Version ]] || \ + [[ ${Version} != "${PMODULES_VERSION}" ]] || \ + [[ ! -v PMODULES_ENV ]] || \ + [[ -z ${PMODULES_ENV} ]]; then declare _tmp_loaded_modules_="${LOADEDMODULES}" declare _tmp_lmfiles_="${_LMFILES_}" @@ -3525,27 +3556,15 @@ if [[ -z ${Version} ]] || [[ ${Version} != ${PMODULES_VERSION} ]]; then fi # we need to handle help text and options for sub-cmd aliases -subcommand=${Subcommands[${subcommand}]} - -# We need a tmp-file in the following sub-commands. It will be removed -# in the exit function if exists. -case ${subcommand} in - load|purge|search|swap|whatis|apropos ) - declare -r tmpfile=$( ${mktemp} /tmp/Pmodules.XXXXXX ) \ - || std::die 1 "Oops: unable to create tmp file!" - ;; - * ) - declare -r tmpfile='' - ;; -esac +SubCommand=${Subcommands[${SubCommand}]} # parse arguments of the sub-command and call it -temp=$("${getopt}" --name="${CMD}" ${Options[${subcommand}]} -- "${opts[@]}" "$@" ) \ - || print_help "${subcommand}" +temp=$("${getopt}" --name="${CMD}" ${Options[${SubCommand}]} -- "${opts[@]}" "$@" ) \ + || print_help "${SubCommand}" eval set -- "${temp}" unset temp -subcommand_${Subcommands[$subcommand]} "$@" +"subcommand_${SubCommand}" "$@" # Local Variables: # mode: sh diff --git a/build b/build index 8b1828f..7d22df5 100755 --- a/build +++ b/build @@ -25,8 +25,6 @@ shopt -s nullglob declare -r BOOTSTRAP_DIR="$(cd "$(dirname "$0")" && pwd -P)" declare -r SRC_DIR="${BOOTSTRAP_DIR}/Pmodules" -source "${SRC_DIR}/libstd.bash" || { echo "Oops!" 1>&2; exit 42; } - declare -r PMOD_DIR="Tools/Pmodules/${VERSION}" # config directory and file relative to install root declare -rx CONFIG_DIR='config' @@ -40,6 +38,61 @@ declare -rx DEFAULT_INSTALL_ROOT='/opt/psi' declare -rx DEFAULT_DISTFILES_DIR='var/distfiles' declare -rx DEFAULT_TMP_DIR='var/tmp/${USER}' +std::log() { + local -ri fd=$1 + local -r fmt="$2" + shift 2 + printf -- "${fmt}" "$@" 1>&$fd + printf -- "\n" 1>&$fd +} + +std::info() { + std::log 2 "$1" "${@:2}" +} + +std::error() { + std::log 2 "$1" "${@:2}" +} + +std::debug() { + [[ -v 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 +} +std::parse_yaml() { + # + # parse a YAML file + # See: https://gist.github.com/pkuczynski/8665367 + # + local -r fname="$1" + local -r prefix="$2" + local s='[[:space:]]*' w='[a-zA-Z0-9_]*' fs=$(echo @|tr @ '\034') + sed -ne "s|^\($s\)\($w\)$s:$s\"\(.*\)\"$s\$|\1$fs\2$fs\3|p" \ + -e "s|^\($s\)\($w\)$s:$s\(.*\)$s\$|\1$fs\2$fs\3|p" "${fname}" | + awk -F$fs '{ + indent = length($1)/2; + vname[indent] = $2; + for (i in vname) { + if (i > indent) {delete vname[i]} + } + if (length($3) > 0) { + vn=""; + for (i=0; i "${PREFIX}/bin/modbuild" chmod 0755 "${PREFIX}/bin/modbuild" - sed "${sed_cmd}" "${SRC_DIR}/modmanage.in" \ - > "${PREFIX}/bin/modmanage" - chmod 0755 "${PREFIX}/bin/modmanage" - sed "${sed_cmd}" "${SRC_DIR}/modmanage.bash.in" \ - > "${PREFIX}/libexec/modmanage.bash" - chmod 0755 "${PREFIX}/libexec/modmanage.bash" - test -e "${INSTALL_ROOT}/${CONFIG_FILE}" || \ install -m 0644 "$_" "${INSTALL_ROOT}/${CONFIG_DIR}" diff --git a/config/Pmodules.yaml.in b/config/Pmodules.yaml.in index f58e49b..d2a4d23 100644 --- a/config/Pmodules.yaml.in +++ b/config/Pmodules.yaml.in @@ -1,9 +1,7 @@ DefaultGroups: Tools:Programming DefaultReleaseStages: stable -ReleaseStages: unstable:stable:deprecated TmpDir: @PMODULES_TMPDIR@ DistfilesDir: @PMODULES_DISTFILESDIR@ -SysCollectionsDir: @INSTALL_ROOT@/collections Overlays: base: install_root: @INSTALL_ROOT@ diff --git a/recipes/120-7z b/recipes/120-7z index 1c92d5c..3c855ab 100755 --- a/recipes/120-7z +++ b/recipes/120-7z @@ -19,7 +19,7 @@ cd 'CPP/7zip/Bundles/Alone2' #--- # compile & install make -j -f ../../cmpl_gcc.mak -cp b/g/7zz "${PREFIX}/${UTILBIN_DIR}" +cp b/g/7zz "${PREFIX}/${UTILBIN_DIR}/sevenz" #--- # post-install