#!/bin/bash declare -r __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}"/*/"${__MODULEFILES_DIR__}"; do local group="${dir%/*}" group="${group##*/}" if [[ ! -v GroupDepths[${group}] ]]; then compute_group_depth depth "${dir}" GroupDepths[$group]=${depth} fi Dir2OverlayMap[${dir%/"${__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: