Files
Pmodules/Pmodules/libpmodules.bash.in
T

346 lines
9.1 KiB
Bash

#!/bin/bash
declare PMODULES_MODULEFILES_DIR='modulefiles'
declare PMODULES_VERSION='@PMODULES_VERSION@'
declare -A GroupDepths=(['none']=0)
declare -a Overlays=()
declare -A OverlayInfo
declare -A Dir2OverlayMap
declare -r ol_normal='n'
declare -r ol_hiding='h'
declare -r ol_replacing='r'
#
# compute depth of modulefile directory.
#
# Args:
# $1: absolute path of a modulefile directory
#
compute_group_depth () {
local -n result="$1"
local -r dir="$2"
if [[ ! -d "${dir}" ]]; then
${mkdir} -p "${dir}" || \
std::die 1 "Cannot create directory -- ${dir}"
fi
local group=${dir%/*}
local group=${group##*/}
result=$(${find} "${dir}" -depth \( -type f -o -type l \) \
-printf "%d" -quit 2>/dev/null)
(( result-=2 )) || :
# if a group doesn't contain a modulefile, depth is negativ
# :FIXME: better solution?
(( result < 0 )) && (( result = 0 )) || :
}
#
# (Re-)Scan available groups in given overlays and compute group depth's
#
# Args:
# $@: overlay names
#
scan_groups () {
local ol
local depth
for ol in "$@"; do
local modulefiles_root="${OverlayInfo[${ol}:modulefiles_root]}"
local dir
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}"
done
done
GroupDepths['none']=0
}
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'
['modulepath']=''
)
yml::die_parsing(){
std::die 3 "error parsing YAML:\n----\n$1\n----"
}
yml::die_type_error(){
std::die 3 "%s" \
"Value of '$1' must be of type '$2', but is '$3'!"
}
yml::die_invalid_key(){
std::die 3 "%s -- %s\n%s" \
"Invalid key in configuration" \
"$1" "$2"
}
yml::die_read_file(){
std::die 3 "Cannot read file '$1'. Please check with yamllint!"
}
yml::read_file(){
local -n yml_content="$1"
local -- yml_fname="$2"
local -- yml_node="$3"
yml_content=$( ${yq} -Ne e "${yml_node}|explode(.)" "${yml_fname}" 2>/dev/null ) || \
yml::die_read_file "${yml_fname}"
}
yml::get_keys(){
local -n yml_keys="$1"
local -n yml_input="$2"
local -- yml_node="$3"
local -- str=''
str="$( ${yq} -e "${yml_node}|keys|.[]" 2>/dev/null <<<"${yml_input}")" || \
{ yml_keys=(); return 0; };
readarray -t yml_keys <<<"${str}"
}
yml::get_type(){
local -n yml_type="$1"
local -n yml_input="$2"
local -- yml_node="$3"
yml_type="$( ${yq} -e "${yml_node}|type" 2>/dev/null <<<"${yml_input}")" || \
yml::die_parsing "${yml_input}"
}
yml::get_value(){
local -n yml_val="$1"
local -n yml_input="$2"
local -- yml_node="$3"
local -- yml_expected_type="$4"
local -- type=''
type="$( ${yq} -e "${yml_node}|type" 2>/dev/null <<<"${yml_input}")" || \
yml::die_parsing "${yml_input}"
[[ "${type}" == "${yml_expected_type}" ]] || \
yml::die_type_error "${yml_node}" "${yml_expected_type:2}" "${type:2}"
yml_val=$( ${yq} -e "${yml_node}" 2>/dev/null <<<"${yml_input}" ) || \
return 1
}
yml::get_seq_length(){
local -n yml_seq_length="$1" # [out] number of variants
local -n yml_input="$2" # [in] YAML input
local -- yml_node="$3" # [in] node
yml_seq_length=$(${yq} -e "${yml_node}|length" 2>/dev/null <<<"${yml_input}") || \
yml::die_parsing "${yml_input}"
}
yml::get_seq(){
local -n yml_val="$1"
local -n yml_input="$2"
local -- yml_node="$3"
local -- type=''
type=$( ${yq} -e "${yml_node}|type" 2>/dev/null <<<"${yml_input}")
if [[ "${type:2}" == 'null' ]]; then
yml_val=''
return 0
fi
[[ "${type}" == '!!seq' ]] || \
yml::die_type_error "${yml_node}" 'seq' "${type:2}"
local -i length=0
length=$(${yq} -e "${yml_node}|length" 2>/dev/null <<<"${yml_input}")
if (( length == 0 )); then
yml_val=''
return 0
fi
yml_val=$( ${yq} -e "${yml_node}[]" 2>/dev/null <<<"${yml_input}" ) || \
return 1
}
pm::read_config(){
: "
Read Pmodules configuration files '${PMODULES_ROOT}/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"
Overlays+=( "${ol_name}" )
# init overlay with defaults
for key in "${!OverlayConfigKeys[@]}"; do
OverlayInfo[${ol_name}:${key}]="${OverlayConfigKeys[${key}]}"
done
# get keys in YAML input
local -- node=".Overlays.${ol_name}"
local -- key=''
local -a keys=()
yml::get_keys keys yaml_input "${node}"
local -- value=''
for key in "${keys[@]}"; do
case ${key,,} in
install_root )
yml::get_value value yaml_input "${node}.${key}" '!!str'
OverlayInfo[${ol_name}:install_root]=$(${envsubst} <<< "${value}")
mkdir -p "${OverlayInfo[${ol_name}:install_root]}" 2>/dev/null
[[ -d ${OverlayInfo[${ol_name}:install_root]} ]] || \
std::die 3 \
"Invalid installation root directory for overlay '${ol_name}' -- ${value}"
;;
modulefiles_root )
yml::get_value value yaml_input "${node}.${key}" '!!str'
OverlayInfo[${ol_name}:modulefiles_root]=$(${envsubst} <<< "${value}")
mkdir -p "${OverlayInfo[${ol_name}:modulefiles_root]}" 2>/dev/null
[[ -d ${OverlayInfo[${ol_name}:modulefiles_root]} ]] || \
std::die 3 \
"Invalid modulefiles root directory for overlay '${ol_name}' -- ${value}"
;;
type )
yml::get_value value yaml_input "${node}.${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 )
yml::get_seq value yaml_input "${node}.${key}" '!!seq'
local -a tmp_array=()
readarray -t tmp_array <<<${value}
local excludes=''
printf -v excludes "%s:" "${tmp_array[@]}"
OverlayInfo[${ol_name}:excludes]=$(${envsubst} <<<"${excludes%:}" )
;;
modulepath )
yml::get_seq value yaml_input "${node}.${key}" '!!seq'
local -a tmp_array=()
readarray -t tmp_array <<<${value}
local modulepath=''
printf -v modulepath "%s:" "${tmp_array[@]}"
OverlayInfo[${ol_name}:modulepath]=$(${envsubst} <<< "${modulepath%:}")
;;
* )
std::die 3 "%s -- %s\n%s" \
"Invalid key in configuration" \
"${key}" "${yaml_input}"
;;
esac
done
OverlayInfo[${ol_name}:used]='no'
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(){
: "
Get Pmodules configuration.
Args:
$1 Pmodules configuration file
"
local -r config_file="$1"
local -- yaml_input=''
yml::read_file yaml_input "${config_file}" '.'
local -- key=''
local -a keys=()
yml::get_keys keys yaml_input '.'
for key in "${keys[@]}"; do
case ${key,,} in
defaultgroups | default_groups )
yml::get_value DefaultGroups yaml_input ".${key}" '!!str'
;;
defaultreleasestages | default_reltages )
yml::get_value DefaultReleaseStages yaml_input ".${key}" '!!str'
;;
tmpdir | tmp_dir )
yml::get_value TmpDir yaml_input ".${key}" '!!str'
;;
distfilesdir | download_dir )
yml::get_value DistfilesDir yaml_input ".${key}" '!!str'
;;
overlays )
local -- overlay=''
local -a overlays=()
yml::get_keys overlays yaml_input ".${key}"
for overlay in "${overlays[@]}"; do
get_config_of_overlay "${yaml_input}" "${overlay}"
done
;;
* )
yml::die_invalid_key "${key}" "${yaml_input}"
;;
esac
done
}
Overlays=()
# system config file
local -- sys_config_file="${PMODULES_HOME%%/Tools*}/config/Pmodules.yaml"
if [[ -v PMODULES_CONFIG_FILE && -n "${PMODULES_CONFIG_FILE}" ]]; then
sys_config_file="${PMODULES_HOME%%/Tools*}/config/${PMODULES_CONFIG_FILE}"
fi
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
# tab-width: 8
# End: