|
| 1 | +#!/usr/bin/env bash |
| 2 | +VERSION="1.0.0" |
| 3 | + |
| 4 | +# Logging |
| 5 | +LOG_LEVEL="INFO" |
| 6 | + |
| 7 | +timestamp () { |
| 8 | + date "+%Y-%m-%d %H:%M:%S" |
| 9 | +} |
| 10 | + |
| 11 | +log () { |
| 12 | + # Simple logger function |
| 13 | + declare -A levels=([DEBUG]=0 [INFO]=1 [WARN]=2 [ERROR]=3) |
| 14 | + msg_type="${1:-INFO}" |
| 15 | + msg_body="${2:-'null'}" |
| 16 | + |
| 17 | + [ ${levels[$msg_type]} ] || log "ERROR" "Unknown log level $msg_type" |
| 18 | + |
| 19 | + # ignore messages below log level |
| 20 | + [ ${levels[$msg_type]} -lt ${levels[$LOG_LEVEL]} ] && return 0 |
| 21 | + # print log message to standard error |
| 22 | + echo "$(timestamp) [$msg_type] $msg_body" >&2 |
| 23 | + # exit after any error message |
| 24 | + [ $msg_type == "ERROR" ] && exit 1 |
| 25 | +} |
| 26 | + |
| 27 | +# Supported CPU specifications |
| 28 | +update_arch_specs(){ |
| 29 | + # Add contents of given spec file into an array |
| 30 | + # 1: name of array with CPU arch specs |
| 31 | + # 2: spec file with the additional specs |
| 32 | + |
| 33 | + [ -z "$1" ] && echo "[ERROR] update_arch_specs: missing array in argument list" >&2 && exit 1 |
| 34 | + local -n arch_specs=$1 |
| 35 | + |
| 36 | + [ ! -f "$2" ] && echo "[ERROR] update_arch_specs: spec file not found: $2" >&2 && exit 1 |
| 37 | + local spec_file="$2" |
| 38 | + while read spec_line; do |
| 39 | + # format spec line as an array and append it to array with all CPU arch specs |
| 40 | + arch_specs+=("(${spec_line})") |
| 41 | + # remove comments from spec file |
| 42 | + done < <(sed -E 's/(^|[\s\t])#.*$//g;/^\s*$/d' "$spec_file") |
| 43 | +} |
| 44 | + |
| 45 | +# CPU specification of host system |
| 46 | +get_cpuinfo(){ |
| 47 | + # Return the value from cpuinfo for the matching key |
| 48 | + # 1: string with key pattern |
| 49 | + |
| 50 | + [ -z "$1" ] && log "ERROR" "get_cpuinfo: missing key pattern in argument list" |
| 51 | + cpuinfo_pattern="^${1}\s*:\s*" |
| 52 | + |
| 53 | + # case insensitive match of key pattern and delete key pattern from result |
| 54 | + grep -i "$cpuinfo_pattern" ${EESSI_PROC_CPUINFO:-/proc/cpuinfo} | tail -n 1 | sed "s/$cpuinfo_pattern//i" |
| 55 | +} |
| 56 | + |
| 57 | +check_allinfirst(){ |
| 58 | + # Return true if all given arguments after the first are found in the first one |
| 59 | + # 1: reference string of space separated values |
| 60 | + # 2,3..: each additional argument is a single value to be found in the reference string |
| 61 | + |
| 62 | + [ -z "$1" ] && log "ERROR" "check_allinfirst: missing argument with reference string" |
| 63 | + reference="$1" |
| 64 | + shift |
| 65 | + |
| 66 | + for candidate in "$@"; do |
| 67 | + [[ " $reference " == *" $candidate "* ]] || return 1 |
| 68 | + done |
| 69 | + return 0 |
| 70 | +} |
| 71 | + |
| 72 | +cpupath(){ |
| 73 | + # Identify the best matching CPU architecture from a list of supported specifications for the host CPU |
| 74 | + # Return the path to the installation files in EESSI of the best matching architecture |
| 75 | + local cpu_arch_spec=() |
| 76 | + |
| 77 | + # Identify the host CPU architecture |
| 78 | + local machine_type=${EESSI_MACHINE_TYPE:-$(uname -m)} |
| 79 | + log "DEBUG" "cpupath: Host CPU architecture identified as '$machine_type'" |
| 80 | + |
| 81 | + # Populate list of supported specs for this architecture |
| 82 | + case $machine_type in |
| 83 | + "x86_64") local spec_file="eessi_arch_x86.spec";; |
| 84 | + "aarch64") local spec_file="eessi_arch_arm.spec";; |
| 85 | + "ppc64le") local spec_file="eessi_arch_ppc.spec";; |
| 86 | + *) log "ERROR" "cpupath: Unsupported CPU architecture $machine_type" |
| 87 | + esac |
| 88 | + # spec files are located in a subfolder with this script |
| 89 | + local base_dir=$(dirname $(realpath $0)) |
| 90 | + update_arch_specs cpu_arch_spec "$base_dir/arch_specs/${spec_file}" |
| 91 | + |
| 92 | + # Identify the host CPU vendor |
| 93 | + local cpu_vendor_tag="vendor[ _]id" |
| 94 | + local cpu_vendor=$(get_cpuinfo "$cpu_vendor_tag") |
| 95 | + log "DEBUG" "cpupath: CPU vendor of host system: '$cpu_vendor'" |
| 96 | + |
| 97 | + # Identify the host CPU flags or features |
| 98 | + local cpu_flag_tag='flags' |
| 99 | + # cpuinfo systems print different line identifiers, eg features, instead of flags |
| 100 | + [ "${cpu_vendor}" == "ARM" ] && cpu_flag_tag='flags' |
| 101 | + [ "${machine_type}" == "aarch64" ] && [ "${cpu_vendor}x" == "x" ] && cpu_flag_tag='features' |
| 102 | + [ "${machine_type}" == "ppc64le" ] && cpu_flag_tag='cpu' |
| 103 | + |
| 104 | + local cpu_flags=$(get_cpuinfo "$cpu_flag_tag") |
| 105 | + log "DEBUG" "cpupath: CPU flags of host system: '$cpu_flags'" |
| 106 | + |
| 107 | + # Default to generic CPU |
| 108 | + local best_arch_match="generic" |
| 109 | + |
| 110 | + # Iterate over the supported CPU specifications to find the best match for host CPU |
| 111 | + # Order of the specifications matters, the last one to match will be selected |
| 112 | + for arch in "${cpu_arch_spec[@]}"; do |
| 113 | + eval "arch_spec=$arch" |
| 114 | + if [ "${cpu_vendor}x" == "${arch_spec[1]}x" ]; then |
| 115 | + # each flag in this CPU specification must be found in the list of flags of the host |
| 116 | + check_allinfirst "${cpu_flags[*]}" ${arch_spec[2]} && best_arch_match=${arch_spec[0]} && \ |
| 117 | + log "DEBUG" "cpupath: host CPU best match updated to $best_arch_match" |
| 118 | + fi |
| 119 | + done |
| 120 | + |
| 121 | + log "INFO" "cpupath: best match for host CPU: $best_arch_match" |
| 122 | + echo "$best_arch_match" |
| 123 | +} |
| 124 | + |
| 125 | +# Parse command line arguments |
| 126 | +USAGE="Usage: eessi_archdetect.sh [-h][-d] <action>" |
| 127 | + |
| 128 | +while getopts 'hdv' OPTION; do |
| 129 | + case "$OPTION" in |
| 130 | + h) echo "$USAGE"; exit 0;; |
| 131 | + d) LOG_LEVEL="DEBUG";; |
| 132 | + v) echo "eessi_archdetect.sh v$VERSION"; exit 0;; |
| 133 | + ?) echo "$USAGE"; exit 1;; |
| 134 | + esac |
| 135 | +done |
| 136 | +shift "$(($OPTIND -1))" |
| 137 | + |
| 138 | +ARGUMENT=${1:-none} |
| 139 | + |
| 140 | +case "$ARGUMENT" in |
| 141 | + "cpupath") cpupath; exit;; |
| 142 | + *) echo "$USAGE"; log "ERROR" "Missing <action> argument";; |
| 143 | +esac |
0 commit comments