diff --git a/CHANGELOG.md b/CHANGELOG.md index f5759b8..9bc8906 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,86 @@ # Changelog of Pmodules +## Version 1.1.10 +* **modulecmd** + * *User visible changes* + * ... + * *Internal changes and fixes* + * ... + +* **build-system** + * *User visible changes* + * ... + * *Internal changes and fixes* + * ... + +* **other changes** + * ... + +## Version 1.1.9 +* **modulecmd** + * *User visible changes* + * Overlay info added to output of sub-command `search`. + * Output of `module search --verbose` revised for better readability. + * *Internal changes and fixes* + * The shell`s init file is sourced, when Pmodules is loaded as module. + This is required if there are changes in the module function or too + define new shell functions. + * A bug in `libmodules.tcl:module-addgroup()` which crashed + `module load ...` has been fixed. + * In versions before 1.1.9 a colon at the beginning or end of `MODULEPATH` + crashed the module function. This has been fixed. + +* **build-system** + * *User visible changes* + * The command `modbuild` is now defined as shell function analog to + the `module` command. The main reason to introduce this function + is due to the fact that Bash version 5 or newer is now required + by `modbuild`. The function `modbuild` load Bash 5.x as module + before calling the modbuild-script. If you want to use the script + directly, a Bash binary with version 5.x must be in PATH. + * If a build-script is in the current working directory, + `modbuild` can now be called without specifying the build-script. + * In case of an error in a build-step the build process did not + abort as it should. This has been fixed. + * The option `--overlay` can now be used + - to define an overlay if legacy variants files are used + - to override the overlay in a YAML variants file. + * The new keyword `with` has been introduced in YAML variants file + to specified hierarchical dependencies. + +* **Internal changes and fixes** + * bugfix in setting `PATH` + * requires bash 5 or later + +## Version 1.1.8 +* **modulecmd** + * *User visible changes* + * configuration in YAML files + * modulefiles and software must not + have a common root directory + * the installation root must be specified, it doesn`t default + to the base 'overlay' any more. + * zsh initialisation fixed. + * *Internal changes and fixes* + * std::upvar() replaced with reference variables in part of the + code. + * environment variable `PMODULES_ROOT` removed. + * unsetting aliases fixed. + * update to bash 5.1.16 + * update to findutils 4.9 (macOS only) + * minor fixes +* **build-system** + * *User visible changes* + * YAML format for variants files + * *Internal changes and fixes* + * use lib `libpmodules.bash` + * bugfixes +* **modmanage** + * *User visible changes* + * none, support for overlays still missing + * *Internal changes and fixes* + * none + ## Version 1.1.7 * **modulecmd** * list of available overlays in subcommand `use` is now better readable diff --git a/Pmodules/bash b/Pmodules/bash index 9f0963a..4b826b1 100644 --- a/Pmodules/bash +++ b/Pmodules/bash @@ -29,11 +29,9 @@ fi unset MODULE_VERSION unset MODULE_VERSION_STACK unset MODULESHOME -unset PMODULES_ENV declare -x PMODULES_DIR="${PMODULES_HOME}" - ############################################################################# # implement module comand as shell function # @@ -43,6 +41,11 @@ module() { } export -f module +modbuild(){ + "${PMODULES_HOME}/bin/modbuild" "$@" +} +export -f modbuild + # Local Variables: # mode: sh # sh-basic-offset: 8 diff --git a/Pmodules/libmodules.tcl b/Pmodules/libmodules.tcl index b4ef6e2..942ae33 100644 --- a/Pmodules/libmodules.tcl +++ b/Pmodules/libmodules.tcl @@ -43,15 +43,15 @@ proc _pmodules_parse_pmodules_env { } { Dir2OverlayMap { array set ::Dir2OverlayMap [regsub -all {[]=[]} $value " "] } - OverlayDict { - array set ::OverlayDict [regsub -all {[]=[]} $value " "] + OverlayInfo { + array set ::OverlayInfo [regsub -all {[]=[]} $value " "] } - OverlayList { + UsedOverlays { array set tmp [regsub -all {[]=[]} $value " "] - set ::OverlayList {} + set ::UsedOverlays {} set l [lsort [array names tmp]] foreach k $l { - lappend ::OverlayList $tmp($k) + lappend ::UsedOverlays $tmp($k) } } UsedGroups { @@ -79,9 +79,10 @@ proc module-addgroup { group } { if { [module-info mode load] } { set overlays_to_add {} - foreach overlay $::OverlayList { + foreach overlay $::UsedOverlays { lappend overlays_to_add $overlay - set ol_type [lindex [split $::OverlayDict($overlay) ":"] 0] + set ol_type $::OverlayInfo($overlay:type) + debug "ol_type=$ol_type" if { [string compare $ol_type $::ol_replacing] == 0 } { break } @@ -91,10 +92,11 @@ proc module-addgroup { group } { debug "group=$group" debug "::variant=$::variant" set dir [file join \ - $overlay \ + $::OverlayInfo($overlay:mod_root) \ $group \ $::MODULEFILES_DIR \ {*}$::variant] + debug "dir=$dir" if { [file isdirectory $dir] } { debug "prepend $dir to MODULEPATH " prepend-path MODULEPATH $dir @@ -122,7 +124,7 @@ proc module-addgroup { group } { debug "mode=remove: no orphan modules to unload" } debug "mode=remove: $env(MODULEPATH)" - foreach overlay $::OverlayList { + foreach overlay $::UsedOverlays { set dir [file join \ $overlay \ $group \ @@ -325,22 +327,22 @@ proc ModulesHelp { } { # proc _find_overlay { modulefile_components } { debug "_find_overlay()" - foreach ol $::OverlayList { + foreach ol $::UsedOverlays { debug "$ol" - set ol_dir $::OverlayDict(${ol}:mod_root) - if { [string range $ol_dir end end] == "/" } { - set ol_dir [string range $ol_dir 0 end-1] + 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] } - debug "$ol_dir" - set ol_dir_splitted [file split $ol_dir] + debug "$ol_mod_root" + set ol_mod_root_splitted [file split $ol_mod_root] set modulefile_root [file join \ {*}[lrange \ $modulefile_components \ - 0 [expr [llength $ol_dir_splitted] - 1]]] + 0 [expr [llength $ol_mod_root_splitted] - 1]]] debug "$modulefile_root" - if { [string compare $ol_dir $modulefile_root] == 0 } { - debug "$ol_dir_splitted" - return $ol_dir_splitted + if { [string compare $ol_mod_root $modulefile_root] == 0 } { + debug "$ol_mod_root_splitted" + return $ol_mod_root_splitted } } debug "not found" @@ -372,8 +374,8 @@ proc _pmodules_init_global_vars { } { set modulefile_splitted [file split $::ModulesCurrentModulefile] - set ol_dir_splitted [_find_overlay ${modulefile_splitted}] - set rel_modulefile [lrange $modulefile_splitted [llength $ol_dir_splitted] end] + set ol_mod_root_splitted [_find_overlay ${modulefile_splitted}] + set rel_modulefile [lrange $modulefile_splitted [llength $ol_mod_root_splitted] end] set group [lindex $rel_modulefile 0] set GROUP "${group}" set name [lindex $modulefile_splitted end-1] @@ -384,11 +386,11 @@ proc _pmodules_init_global_vars { } { set V_RELEASE [lindex [split $tmp _] 0] lassign [split $V_PKG .] V_MAJOR V_MINOR V_PATCHLVL set variant [lrange $rel_modulefile 2 end] - set mod_root [file join {*}$ol_dir_splitted] + set mod_root [file join {*}$ol_mod_root_splitted] debug "mod_root=$mod_root" set ol $::Dir2OverlayMap($mod_root) debug "ol=$ol" - set install_prefix [file split $::OverlayDict(${ol}:inst_root)] + set install_prefix [file split $::OverlayInfo(${ol}:inst_root)] set prefix "$install_prefix $group [lreverse_n $variant 2]" set PREFIX [file join {*}$prefix] debug "PREFIX=$PREFIX" diff --git a/Pmodules/libpbuild.bash b/Pmodules/libpbuild.bash index 165d84b..ddb954f 100644 --- a/Pmodules/libpbuild.bash +++ b/Pmodules/libpbuild.bash @@ -1,22 +1,5 @@ #!/bin/bash -#............................................................................. -# -# We need GNU versions of the following utilities. This code works -# well on Linux and Mac OS X with MacPorts. -# :FIXME: implement a smarter, portable solution. -# -shopt -s expand_aliases -unalias -a - -__path=$(which gsed 2>/dev/null || : ) -if [[ $__path ]]; then - alias sed=$__path -else - alias sed=$(which sed 2>/dev/null) -fi -unset __path - #............................................................................. # disable auto-echo feature of 'cd' unset CDPATH @@ -28,8 +11,9 @@ declare -r FNAME_RDEPS='.dependencies' declare -r FNAME_IDEPS='.install_dependencies' declare -r FNAME_BDEPS='.build_dependencies' -#............................................................................. -declare -A SOURCE_UNPACK_DIRS +# relative path of documentation +# abs. path is "${PREFIX}/${_docdir}/${module_name}" +declare -r _DOCDIR='share/doc' #............................................................................. # @@ -37,17 +21,106 @@ declare -A SOURCE_UNPACK_DIRS # # $1 exit code # -set -o errexit +#set -o errexit -error_handler() { +_error_handler() { local -i ec=$? std::die ${ec} "Oops" } +readonly -f _error_handler -trap "error_handler" ERR +trap "_error_handler" ERR -declare configure_with='undef' +#.............................................................................. +# +# write number of cores to stdout +# +_get_num_cores() { + case "${OS}" in + Linux ) + ${grep} -c ^processor /proc/cpuinfo + ;; + Darwin ) + ${sysctl} -n hw.ncpu + ;; + * ) + std::die 1 "OS ${OS} is not supported\n" + ;; + esac +} +readonly -f _get_num_cores + +#.............................................................................. +# global variables which can be set/overwritten by command line args +# and their corresponding functions +# +declare force_rebuild='' +pbuild.force_rebuild() { + force_rebuild="$1" +} +readonly -f pbuild.force_rebuild + +declare dry_run='' +pbuild.dry_run() { + dry_run="$1" +} +readonly -f pbuild.dry_run + +declare enable_cleanup_build='' +pbuild.enable_cleanup_build() { + enable_cleanup_build="$1" +} +readonly -f pbuild.enable_cleanup_build + +declare enable_cleanup_src='' +pbuild.enable_cleanup_src() { + enable_cleanup_src="$1" +} +readonly -f pbuild.enable_cleanup_src + +declare build_target='' +pbuild.build_target() { + build_target="$1" +} +readonly -f pbuild.build_target + +declare opt_update_modulefiles='' +pbuild.update_modulefiles() { + opt_update_modulefiles="$1" +} +readonly -f pbuild.update_modulefiles + +# number of parallel make jobs +declare -i JOBS=0 +pbuild.jobs() { + if (( $1 == 0 )); then + JOBS=$(_get_num_cores) + (( JOBS > 10 )) && JOBS=10 || : + else + JOBS="$1" + fi +} +readonly -f pbuild.jobs + +declare system='' +pbuild.system() { + system="$1" +} +readonly -f pbuild.system + +declare verbose='' +pbuild.verbose() { + verbose="$1" +} +readonly -f pbuild.verbose + + +############################################################################### +# +# function in the "namespace" (with prefix) 'pbuild::' can be used in +# build-scripts +# #.............................................................................. # @@ -81,120 +154,53 @@ pbuild::version_compare () { done return 0 } +readonly -f pbuild::version_compare +#.............................................................................. +# version less than +# +# return 0 if version passed in $1 is older then $2 +# pbuild::version_lt() { pbuild::version_compare "$1" "$2" (( $? == 2 )) } +readonly -f pbuild::version_lt +#.............................................................................. +# version less than or equal +# +# return 0 if version passed in $1 is older or equal then $2 +# pbuild::version_le() { pbuild::version_compare "$1" "$2" local -i exit_code=$? (( exit_code == 0 || exit_code == 2 )) } +readonly -f pbuild::version_le - +#.............................................................................. +# version greater than +# +# return 0 if version passed in $1 is newer then $2 +# pbuild::version_gt() { pbuild::version_compare "$1" "$2" (( $? == 1 )) local -i exit_code=$? (( exit_code == 0 || exit_code == 1 )) } +readonly -f pbuild::version_gt +#.............................................................................. +# version greater than +# +# return 0 if version passed in $1 and $2 are equal +# pbuild::version_eq() { pbuild::version_compare "$1" "$2" } - -#.............................................................................. -# -# The following variables are available in build-blocks and set read-only -# :FIXME: do we have to export them? - -# install prefix of module. -declare -x PREFIX='' - -declare -r OS=$(uname -s) - -pbuild::get_num_cores() { - case "${OS}" in - Linux ) - echo $(grep -c ^processor /proc/cpuinfo) - ;; - Darwin ) - echo $(sysctl -n hw.ncpu) - ;; - * ) - std::die 1 "OS ${OS} is not supported\n" - ;; - esac -} - -#.............................................................................. -# global variables which can be set/overwritten by command line args - -declare force_rebuild='' -pbuild.force_rebuild() { - force_rebuild="$1" -} - -declare dry_run='' -pbuild.dry_run() { - dry_run="$1" -} - -declare enable_cleanup_build='' -pbuild.enable_cleanup_build() { - enable_cleanup_build="$1" -} - -declare enable_cleanup_src='' -pbuild.enable_cleanup_src() { - enable_cleanup_src="$1" -} - -declare build_target='' -pbuild.build_target() { - build_target="$1" -} - -declare opt_update_modulefiles='' -pbuild.update_modulefiles() { - opt_update_modulefiles="$1" -} - -# number of parallel make jobs -declare -i JOBS=$(pbuild::get_num_cores) -pbuild.jobs() { - if (( $1 == 0 )); then - JOBS=$(pbuild::get_num_cores) - (( JOBS > 10 )) && JOBS=10 || : - else - JOBS="$1" - fi -} - -declare system='' -pbuild.system() { - system="$1" -} - -declare verbose='' -pbuild.verbose() { - verbose="$1" -} - - -# group this module is in (ex: 'Programming') -declare -x GROUP='' - -# name, version and release of module -declare -x module_name='' -declare -x module_version='' -declare -x module_release='' - -# relative path of documentation -# abs. path is "${PREFIX}/${_docdir}/${module_name}" -declare -r _DOCDIR='share/doc' +readonly -f pbuild::version_eq ############################################################################## # @@ -206,6 +212,7 @@ declare -r _DOCDIR='share/doc' pbuild::compile_in_sourcetree() { BUILD_DIR="${SRC_DIR}" } +readonly -f pbuild::compile_in_sourcetree ############################################################################## # @@ -218,6 +225,7 @@ pbuild::compile_in_sourcetree() { pbuild::supported_systems() { SUPPORTED_SYSTEMS+=( "$@" ) } +readonly -f pbuild::supported_systems ############################################################################## # @@ -230,6 +238,7 @@ pbuild::supported_systems() { pbuild::supported_os() { SUPPORTED_OS+=( "$@" ) } +readonly -f pbuild::supported_os ############################################################################## # @@ -242,6 +251,7 @@ pbuild::supported_os() { pbuild::supported_compilers() { SUPPORTED_COMPILERS+=( "$@" ) } +readonly -f pbuild::supported_compilers ############################################################################## # @@ -251,13 +261,14 @@ pbuild::supported_compilers() { # $1: group # pbuild::add_to_group() { - if [[ -z ${1} ]]; then + if (( $# == 0 )); then std::die 42 \ "%s " "${module_name}/${module_version}:" \ "${FUNCNAME}: missing group argument." fi GROUP="$1" } +readonly -f pbuild::add_to_group ############################################################################## # @@ -269,6 +280,7 @@ pbuild::add_to_group() { pbuild::install_docfiles() { MODULE_DOCFILES+=("$@") } +readonly -f pbuild::install_docfiles ############################################################################## # @@ -277,41 +289,85 @@ pbuild::install_docfiles() { # # Arguments: # $1: module name -# $2: optional variable name to return release via upvar +# $2: optional variable name to return release # # Notes: -# The passed module name must be NAME/VERSION! +# The passed module name must be module/version! +# +# Exit codes: +# 0 if module/version is available +# 1 otherwise # pbuild::module_is_avail() { - local "$2" - local uvar="$2" - [[ -n "${uvar}" ]] || uvar="__unused__" 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 - std::upvar "${uvar}" "${output[i+1]}" + if (( $# > 1 )); then + local -n _result="$2" + _result="${output[i+1]}" + fi return 0 fi done return 1 } +readonly -f pbuild::module_is_avail +############################################################################## +# +# Set the download URL and name of downloaded file. +# +# Arguments: +# $1 download URL +# $2 optional file-name (of) pbuild::set_download_url() { local -i _i=${#SOURCE_URLS[@]} SOURCE_URLS[_i]="$1" - SOURCE_NAMES[_i]="$2" + if (( $# > 1 )); then + SOURCE_NAMES[$_i]="${2:-${1##*/}}" + else + SOURCE_NAMES[$_i]="${1##*/}" + fi } +readonly -f pbuild::set_download_url +############################################################################## +# +# Set hash sum for file. +# +# Arguments: +# $1 filen-name:hash-sum +# +# :FIXME: +# Maybe we should use a dictionary in the future. +# pbuild::set_sha256sum() { SOURCE_SHA256_SUMS+=("$1") } +readonly -f pbuild::set_sha256sum +############################################################################## +# +# Unpack file $1 in directory $2 +# +# Arguments: +# $1 file-name +# $2 directory +# pbuild::set_unpack_dir() { SOURCE_UNPACK_DIRS[$1]=$2 } +readonly -f pbuild::set_unpack_dir +############################################################################## +# +# Use this C-compiler +# +# Arguments: +# $1 C-compiler to use. +# pbuild::use_cc() { [[ -x "$1" ]] || std::die 3 \ "%s " "${module_name}/${module_version}:" \ @@ -319,14 +375,63 @@ pbuild::use_cc() { "'$1' is not an executable!" CC="$1" } +readonly -f pbuild::use_cc -pbuild::pre_prep() { - : +############################################################################### +# +pbuild::add_patch() { + [[ -z "$1" ]] && \ + std::die 1 \ + "%s " "${module_name}/${module_version}:" \ + "${FUNCNAME}: missing argument!" + PATCH_FILES+=( "$1" ) + if (( $# >= 2 )); then + PATCH_STRIPS+=( "$2" ) + else + PATCH_STRIPS+=( "${PATCH_STRIP_DEFAULT}" ) + fi } +readonly -f pbuild::add_patch -pbuild::post_prep() { - : +############################################################################### +# +pbuild::set_default_patch_strip() { + [[ -n "$1" ]] || \ + std::die 1 \ + "%s " "${module_name}/${module_version}:" \ + "${FUNCNAME}: missing argument!" + + PATCH_STRIP_DEFAULT="$1" } +readonly -f pbuild::set_default_patch_strip + +############################################################################### +# +pbuild::use_flag() { + [[ "${USE_FLAGS}" =~ ":${1}:" ]] +} +readonly -f pbuild::use_flag + +############################################################################### +# +pbuild::add_configure_args() { + CONFIGURE_ARGS+=( "$@" ) +} +readonly -f pbuild::add_configure_args + +############################################################################### +# +pbuild::use_autotools() { + configure_with='autotools' +} +readonly -f pbuild::use_autotools + +############################################################################### +# +pbuild::use_cmake() { + configure_with='cmake' +} +readonly -f pbuild::use_cmake ############################################################################### # @@ -335,63 +440,57 @@ pbuild::post_prep() { pbuild::prep() { #...................................................................... # - # Find/download tarball for given module. + # download the source file if not already downloaded and validate + # checksum (if known). + # Abort on any error! # # Arguments: - # $1: store file name with upvar here - # $2: download URL - # $3: output filename (can be empty string) - # $4...: download directories + # $1 reference varibale to return result + # $2 download URL + # $3 save downloaded file with this name. If the empty + # string is passed, derive file name from URL + # $4... directories the source file might be already in. If the + # file does not exist in one of these directories, it + # is downloaded and stored in the first given directory. # - # Returns: - # 0 on success otherwise a value > 0 - # - download_with_curl() { - local -r output="$1" - local -r url="$2" - curl \ - -L \ - --output "${output}" \ - "${url}" - if (( $? != 0 )); then - curl \ - --insecure \ + download_source_file() { + download_with_curl() { + local -r output="$1" + local -r url="$2" + ${curl} \ + --location \ + --fail \ --output "${output}" \ "${url}" - fi - } + # :FIXME: How to handle insecure downloads? + #if (( $? != 0 )); then + # curl \ + # --insecure \ + # --output "${output}" \ + # "${url}" + #fi + } - check_hash_sum() { - local -r fname="$1" - local -r expected_hash_sum="$2" - local hash_sum='' + check_hash_sum() { + local -r fname="$1" + local -r expected_hash_sum="$2" + local hash_sum='' - if which 'sha256sum' 1>/dev/null; then - hash_sum=$(sha256sum "${fname}" | awk '{print $1}') - elif which 'shasum' 1>/dev/null; then - hash_sum=$(shasum -a 256 "${fname}" | awk '{print $1}') - else - std::die 42 \ - "%s " "${module_name}/${module_version}:" \ - "Binary to compute SHA256 sum missing!" - fi - test "${hash_sum}" == "${expected_hash_sum}" || \ - std::die 42 \ - "%s " "${module_name}/${module_version}:" \ - "hash-sum missmatch for file '%s'" "${fname}" - } + hash_sum=$(${sha256sum} "${fname}" | awk '{print $1}') + test "${hash_sum}" == "${expected_hash_sum}" || \ + std::die 42 \ + "%s " \ + "${module_name}/${module_version}:" \ + "hash-sum missmatch for file '${fname}'!" + } - download_source_file() { - local "$1" - local var="$1" + local -n _result="$1" local -r url="$2" local fname="$3" shift 3 dirs+=( "$@" ) [[ -n "${fname}" ]] || fname="${url##*/}" - local expr='s/.*\(.tar.bz2\|.tbz2\|.tar.gz\|.tgz\|.tar.xz\|.zip\)/\1/' - local -r extension=$(echo ${fname} | sed "${expr}") local dir='' dirs+=( 'not found' ) for dir in "${dirs[@]}"; do @@ -399,19 +498,20 @@ pbuild::prep() { done if [[ "${dir}" == 'not found' ]]; then dir="${dirs[0]}" - local -r method="${url%:*}" - case "${method}" in - http | https | ftp ) - download_with_curl "${dir}/${fname}" "${url}" - ;; - * ) - std::die 4 \ - "%s " "${module_name}/${module_version}:" \ - "Error in download URL:" \ - "unknown download method '${method}'!" - ;; - esac + download_with_curl "${dir}/${fname}" "${url}" + (( $? == 0 )) || \ + std::die 42 \ + "%s " \ + "${module_name}/${module_version}:" \ + "downloading source file '${fname}' failed!" fi + _result="${dir}/${fname}" + [[ -r "${_result}" ]] || \ + std::die 42 \ + "%s " \ + "${module_name}/${module_version}:" \ + "source file '${_result}' is not readable!" + local sha256_sum='' local hash='' for hash in "${SOURCE_SHA256_SUMS[@]}"; do @@ -422,15 +522,13 @@ pbuild::prep() { if [[ -n "${sha256_sum}" ]]; then check_hash_sum "${dir}/${fname}" "${sha256_sum}" fi - std::upvar "${var}" "${dir}/${fname}" - [[ -r "${dir}/${fname}" ]] } unpack() { local -r file="$1" local -r dir="${2:-${SRC_DIR}}" - tar --directory="${dir}" -xv --strip-components 1 -f "${file}" || { - rm -f "${file}" + ${tar} --directory="${dir}" -xv --strip-components 1 -f "${file}" || { + ${rm} -f "${file}" std::die 4 \ "%s " \ "${module_name}/${module_version}:" \ @@ -447,7 +545,7 @@ pbuild::prep() { "${module_name}/${module_version}:" \ "Appling patch '${PATCH_FILES[_i]}' ..." local -i strip_val="${PATCH_STRIPS[_i]:-${PATCH_STRIP_DEFAULT}}" - patch -p${strip_val} < "${BUILDBLOCK_DIR}/${PATCH_FILES[_i]}" || \ + ${patch} -p${strip_val} < "${BUILDBLOCK_DIR}/${PATCH_FILES[_i]}" || \ std::die 4 \ "%s " \ "${module_name}/${module_version}:" \ @@ -466,7 +564,7 @@ pbuild::prep() { std::die 3 \ "%s " "${module_name}/${module_version}:" \ "Download source not set!" - mkdir -p "${PMODULES_DISTFILESDIR}" + ${mkdir} -p "${PMODULES_DISTFILESDIR}" local i=0 local source_fname for ((i = 0; i < ${#SOURCE_URLS[@]}; i++)); do @@ -479,58 +577,29 @@ pbuild::prep() { std::die 4 \ "%s " "${module_name}/${module_version}:" \ "sources for not found." - unpack "${source_fname}" "${SOURCE_UNPACK_DIRS[${source_fname##*/}]}" + local dir='' + local key="${SOURCE_URLS[i]##*/}" + if [[ -v SOURCE_UNPACK_DIRS[${key}] ]]; then + echo "dir specified" + dir="${SOURCE_UNPACK_DIRS[${SOURCE_URLS[i]##*/}]}" + else + echo "use SRC_DIR" + dir="${SRC_DIR}" + fi + unpack "${source_fname}" "${dir}" done patch_sources # create build directory - mkdir -p "${BUILD_DIR}" -} - -pbuild::add_patch() { - [[ -z "$1" ]] && \ - std::die 1 \ - "%s " "${module_name}/${module_version}:" \ - "${FUNCNAME}: missing argument!" - PATCH_FILES+=( "$1" ) - PATCH_STRIPS+=( "$2" ) -} - -pbuild::set_default_patch_strip() { - [[ -n "$1" ]] || \ - std::die 1 \ - "%s " "${module_name}/${module_version}:" \ - "${FUNCNAME}: missing argument!" - - PATCH_STRIP_DEFAULT="$1" -} - -pbuild::use_flag() { - [[ "${USE_FLAGS}" =~ ":${1}:" ]] + ${mkdir} -p "${BUILD_DIR}" } ############################################################################### # +# Configure the software to be compiled. +# +# Arguments: +# none # -pbuild::pre_configure() { - : -} - -pbuild::set_configure_args() { - CONFIGURE_ARGS+=( "$@" ) -} - -pbuild::add_configure_args() { - CONFIGURE_ARGS+=( "$@" ) -} - -pbuild::use_autotools() { - configure_with='autotools' -} - -pbuild::use_cmake() { - configure_with='cmake' -} - pbuild::configure() { case "${configure_with}" in autotools ) @@ -562,6 +631,7 @@ pbuild::configure() { elif [[ -r "${SRC_DIR}/CMakeLists.txt" ]] && \ [[ "${configure_with}" == 'undef' ]] || \ [[ "${configure_with}" == "cmake" ]]; then + # note: in most/many cases a cmake module is used! cmake \ -DCMAKE_INSTALL_PREFIX="${PREFIX}" \ "${CONFIGURE_ARGS[@]}" \ @@ -577,36 +647,38 @@ pbuild::configure() { fi } -pbuild::post_configure() { - : -} - -pbuild::pre_compile() { - : -} +############################################################################### +# +# Default compile function. +# +# Arguments: +# none +# pbuild::compile() { - make -j${JOBS} || \ + (( JOBS == 0 )) && JOBS=$(_get_num_cores) + ${make} -j${JOBS} || \ std::die 3 \ "%s " "${module_name}/${module_version}:" \ "compilation failed!" } -pbuild::post_compile() { - : -} - -pbuild::pre_install() { - : -} - +############################################################################### +# +# Default install function. +# +# Arguments: +# none +# pbuild::install() { - make install || \ + ${make} install || \ std::die 3 \ "%s " "${module_name}/${module_version}:" \ "compilation failed!" } +############################################################################### +# pbuild::install_shared_libs() { local -r binary="$1" local -r dstdir="$2" @@ -614,19 +686,19 @@ pbuild::install_shared_libs() { install_shared_libs_Linux() { local libs=( $(ldd "${binary}" | \ - awk "/ => \// && /${pattern}/ {print \$3}") ) + ${awk} "/ => \// && /${pattern}/ {print \$3}") ) if [[ -n "${libs}" ]]; then - cp -vL "${libs[@]}" "${dstdir}" || return $? + ${cp} -vL "${libs[@]}" "${dstdir}" || return $? fi return 0 } 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 libs=( $(${otool} -L "${binary}" | \ + ${awk} "/${pattern}/ {print \$1}")) if [[ -n "${libs}" ]]; then - cp -vL "${libs[@]}" "${dstdir}" || return $? + ${cp} -vL "${libs[@]}" "${dstdir}" || return $? fi return 0 } @@ -635,7 +707,7 @@ pbuild::install_shared_libs() { std::die 3 \ "%s " "${module_name}/${module_version}:" \ "${binary}: does not exist or is not executable!" - mkdir -p "${dstdir}" + ${mkdir} -p "${dstdir}" case "${OS}" in Linux ) install_shared_libs_Linux @@ -646,639 +718,18 @@ pbuild::install_shared_libs() { esac } -pbuild::post_install() { - : -} - +############################################################################### # -# The 'do it all' function. +# This is the main entry function called by modbuild! # -pbuild::make_all() { - source "${BUILD_SCRIPT}" - - set -e - local -r logfile="${BUILDBLOCK_DIR}/pbuild.log" - # module name including path in hierarchy and version - # (ex: 'gcc/6.1.0/openmpi/1.10.2' for openmpi compiled with gcc 6.1.0) - local modulefile_dir='' - local modulefile_name='' - - # - # To be able to set environment variables in one of the 'pbuild::TARGET' - # function we cannot use PIPE's like - # pbuild::configure | tee -a ... - # - rm -f "${logfile}" - if [[ "${verbose}" == 'yes' ]]; then - exec > >(tee -a "${logfile}") - else - exec > >(cat >> "${logfile}") - fi - exec 2> >(tee -a "${logfile}" >&2) - - # - # everything set up? - # - [[ -n ${GROUP} ]] || \ - std::die 5 \ - "%s " "${module_name}/${module_version}:" \ - "Module group not set! Aborting ..." - - - # - # helper functions - # - - #...................................................................... - check_supported_systems() { - [[ -z "${SUPPORTED_SYSTEMS}" ]] && return 0 - for sys in "${SUPPORTED_SYSTEMS[@]}"; do - [[ ${sys,,} == ${system,,} ]] && return 0 - done - std::die 1 \ - "%s " "${module_name}/${module_version}:" \ - "Not available for ${system}." - } - - #...................................................................... - check_supported_os() { - [[ -z "${SUPPORTED_OS}" ]] && return 0 - for os in "${SUPPORTED_OS[@]}"; do - [[ ${os,,} == ${OS,,} ]] && return 0 - done - std::die 1 \ - "%s " "${module_name}/${module_version}:" \ - "Not available for ${OS}." - } - - #...................................................................... - check_supported_compilers() { - [[ -z "${SUPPORTED_COMPILERS}" ]] && return 0 - for compiler in "${SUPPORTED_COMPILERS[@]}"; do - [[ ${compiler,,} == ${COMPILER,,} ]] && return 0 - done - std::die 1 \ - "%s " "${module_name}/${module_version}:" \ - "Not available for ${COMPILER}." - } - - #...................................................................... - # - # compute full module name and installation prefix - # - # The following variables are expected to be set: - # GROUP module group - # P module name - # V module version - # variables defining the hierarchical environment like - # COMPILER and COMPILER_VERSION - # - # The following variables are set in this function - # modulefile_dir - # modulefile_name - # PREFIX - # - set_full_module_name_and_prefix() { - join_by() { - local IFS="$1" - shift - echo "$*" - } - - [[ -n ${GROUP} ]] || std::die 1 \ - "${module_name}/${module_version}:" \ - "group not set." - - # define defaults if not set in configuration file - : ${Compiler_HIERARCHY:='${COMPILER}/${COMPILER_VERSION}'} - : ${CUDA_HIERARCHY:='${COMPILER}/${COMPILER_VERSION} cuda/${CUDA_VERSION}'} - : ${MPI_HIERARCHY:='${COMPILER}/${COMPILER_VERSION} ${MPI}/${MPI_VERSION}'} - : ${HDF5_HIERARCHY:='${COMPILER}/${COMPILER_VERSION} ${MPI}/${MPI_VERSION} hdf5/${HDF5_VERSION}'} - : ${HDF5_serial_HIERARCHY:='${COMPILER}/${COMPILER_VERSION} hdf5_serial/${HDF5_SERIAL_VERSION}'} - - # evaluate - local names=() - local vname="${GROUP}_HIERARCHY" - if [[ -n ${!vname} ]]; then - names=( $(eval echo ${!vname}) ) - fi - - modulefile_dir=$(join_by '/' \ - "${ol_dir}/${GROUP}/${PMODULES_MODULEFILES_DIR}" \ - "${names[@]}" \ - "${module_name}") - modulefile_name="${modulefile_dir}/${module_version}" - - PREFIX="${ol_install_dir}/${GROUP}/${module_name}/${module_version}" - local -i i=0 - for ((i=${#names[@]}-1; i >= 0; i--)); do - PREFIX+="/${names[i]}" - done - } - - #...................................................................... - # Select the modulefile to install. Modulefiles can be versioned like - # modulefile-10.2.0 - # modulefile-10.2 - # modulefile-10 - # modulefile - # the most specific modulefile will be selected. Example: - # For a version 10.2.1 the file moduelfile-10.2 would be selected. - # - # Arguments: - # $1 upvar to return the filename - # - # Used gloabal variables: - # VERSIONS - # BUILDBLOCK_DIR - # - find_modulefile() { - local "$1" - local fname='' - local modulefile='' - for fname in "${VERSIONS[@]/#/modulefile-}" 'modulefile'; do - if [[ -r "${BUILDBLOCK_DIR}/${fname}" ]]; then - modulefile="${BUILDBLOCK_DIR}/${fname}" - break; - fi - done - std::upvar $1 "${modulefile}" - [[ -n "${modulefile}" ]] - } - - #...................................................................... - # non-redefinable post-install. Install: - # - documentation files as defined in the build-script - # - modulefile and file with release - # . - post_install() { - #.............................................................. - # install the doc-files specified in the build-script - # - # Arguments: - # none - # - install_doc() { - if [[ -z "${MODULE_DOCFILES}" ]]; then - for f in ${VERSIONS[@]/#/pbuild::install_docfiles_}; do - if typeset -F "$f" 2>/dev/null; then - $f - break - fi - done - - fi - [[ -n "${MODULE_DOCFILES}" ]] || return 0 - local -r docdir="${PREFIX}/${_DOCDIR}/${module_name}" - - std::info \ - "%s " \ - "${module_name}/${module_version}:" \ - "Installing documentation to ${docdir}" - install -m 0755 -d \ - "${docdir}" - install -m0644 \ - "${MODULE_DOCFILES[@]/#/${SRC_DIR}/}" \ - "${docdir}" - return 0 - } - - #.............................................................. - # install build-block files - # - modulefile - # - build-script - # - run-time and build dependencies - # in ${PREFIX}/share/${GROUP}/${module_name} - # - # Arguments: - # none - # - install_pmodules_files() { - local modulefile='' - find_modulefile modulefile || return 0 - - local -r target_dir="${PREFIX}/share/$GROUP/${module_name}" - mkdir -p "${target_dir}" - install -m0644 \ - "${BUILD_SCRIPT}" \ - "${target_dir}" - install -m0644 \ - "${modulefile}" \ - "${target_dir}" - #install -m 0755 \ - # -d "${target_dir}/files" - #install -m0644 \ - # "${variants_file}" \ - # "${target_dir}/files" - - local -r fname="${target_dir}/dependencies" - "${MODULECMD}" bash list -t 2>&1 1>/dev/null | \ - grep -v "Currently Loaded" > "${fname}" || : - } - - #.............................................................. - # write run time dependencies to file - write_runtime_dependencies() { - local -r fname="$1" - shift - std::info \ - "%s " \ - "${module_name}/${module_version}:" \ - "writing run-time dependencies to ${fname} ..." - local dep - echo -n "" > "${fname}" - for dep in "$@"; do - [[ -z $dep ]] && continue - if [[ ! $dep == */* ]]; then - # no version given: derive the version - # from the currently loaded module - dep=$( "${MODULECMD}" bash list -t 2>&1 1>/dev/null \ - | grep "^${dep}/" ) - fi - echo "${dep}" >> "${fname}" - done - } - - #.............................................................. - # for Linux we need a special post-install to solve the - # multilib problem with LIBRARY_PATH on 64-bit systems - post_install_linux() { - std::info \ - "%s " \ - "${module_name}/${module_version}:" \ - "running post-installation for ${OS} ..." - cd "${PREFIX}" - [[ -d "lib" ]] && [[ ! -d "lib64" ]] && ln -s lib lib64 - return 0 - } - - #.............................................................. - cd "${BUILD_DIR}" - [[ "${OS}" == "Linux" ]] && post_install_linux - install_doc - install_pmodules_files - if [[ -n "${runtime_dependencies}" ]]; then - write_runtime_dependencies \ - "${PREFIX}/${FNAME_RDEPS}" \ - "${runtime_dependencies[@]}" - fi - if [[ -n "${install_dependencies}" ]]; then - write_runtime_dependencies \ - "${PREFIX}/${FNAME_IDEPS}" \ - "${install_dependencies[@]}" - fi - if [[ "${bootstrap}" == 'no' ]]; then - install_modulefile - install_release_file - fi - cleanup_build - cleanup_src - std::info \ - "%s " \ - "${module_name}/${module_version}:" \ - "Done ..." - return 0 - } - - #...................................................................... - # Install modulefile in ${pm_root}/${GROUP}/modulefiles/... - # - # Arguments - # none - install_modulefile() { - local src='' - find_modulefile src - if (( $? != 0 )); then - std::info \ - "%s " \ - "${module_name}/${module_version}:" \ - "skipping modulefile installation ..." - return - fi - std::info \ - "%s " \ - "${module_name}/${module_version}:" \ - "installing modulefile '${modulefile_name}' ..." - mkdir -p "${modulefile_dir}" - install -m 0644 "${src}" "${modulefile_name}" - } - - install_release_file() { - local -r release_file="${modulefile_dir}/.release-${module_version}" - - if [[ -r "${release_file}" ]]; then - local release - read release < "${release_file}" - if [[ "${release}" != "${module_release}" ]]; then - std::info \ - "%s " \ - "${module_name}/${module_version}:" \ - "changing release from" \ - "'${release}' to '${module_release}' ..." - echo "${module_release}" > "${release_file}" - fi - else - std::info \ - "%s " \ - "${module_name}/${module_version}:" \ - "setting release to '${module_release}' ..." - echo "${module_release}" > "${release_file}" - fi - } - - cleanup_build() { - [[ ${enable_cleanup_build} == yes ]] || return 0 - [[ "${BUILD_DIR}" == "${SRC_DIR}" ]] && return 0 - { - cd "/${BUILD_DIR}/.." || std::die 42 "Internal error" - [[ "$(pwd)" == "/" ]] && \ - std::die 1 \ - "%s " "${module_name}/${module_version}:" \ - "Oops: internal error:" \ - "BUILD_DIR is set to '/'" - - std::info \ - "%s " \ - "${module_name}/${module_version}:" \ - "Cleaning up '${BUILD_DIR}'..." - rm -rf "${BUILD_DIR##*/}" - }; - return 0 - } - - cleanup_src() { - [[ ${enable_cleanup_src} == yes ]] || return 0 - { - cd "/${SRC_DIR}/.." || std::die 42 "Internal error" - [[ $(pwd) == / ]] && \ - std::die 1 \ - "%s " "${module_name}/${module_version}:" \ - "Oops: internal error:" \ - "SRC_DIR is set to '/'" - std::info \ - "%s " \ - "${module_name}/${module_version}:" \ - "Cleaning up '${SRC_DIR}'..." - rm -rf "${SRC_DIR##*/}" - }; - return 0 - } - - build_target() { - local dir="$1" # src or build directory, depends on target - local target="$2" # prep, configure, compile or install - - if [[ -e "${BUILD_DIR}/.${target}" ]] && \ - [[ ${force_rebuild} != 'yes' ]]; then - 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::${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::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} ) - - for t in "${targets[@]}"; do - # We cd into the dir before calling the function - - # just to be sure we are in the right directory. - # - # Executing the function in a sub-process doesn't - # work because in some function global variables - # might/need to be set. - # - cd "${dir}" - typeset -F "$t" 2>/dev/null && "$t" || : - done - touch "${BUILD_DIR}/.${target}" - } - - #...................................................................... - # build module ${module_name}/${module_version} - build_module() { - std::info \ - "%s " \ - "${module_name}/${module_version}:" \ - "start building" \ - ${with_modules:+with ${with_modules[@]}} \ - "..." - [[ ${dry_run} == yes ]] && std::die 0 "" - - mkdir -p "${SRC_DIR}" - mkdir -p "${BUILD_DIR}" - - std::info \ - "%s " \ - "${module_name}/${module_version}:" \ - "preparing sources ..." - # write stdout and stderr to logfile, stderr to terminal - # write all to logfile and terminal - build_target "${SRC_DIR}" prep - [[ "${build_target}" == "prep" ]] && return 0 - - std::info \ - "%s " \ - "${module_name}/${module_version}:" \ - "configuring ..." - build_target "${BUILD_DIR}" configure - [[ "${build_target}" == "configure" ]] && return 0 - - std::info \ - "%s " \ - "${module_name}/${module_version}:" \ - "compiling ..." - build_target "${BUILD_DIR}" compile - [[ "${build_target}" == "compile" ]] && return 0 - - std::info \ - "%s " \ - "${module_name}/${module_version}:" \ - "installing ..." - mkdir -p "${PREFIX}" - build_target "${BUILD_DIR}" install - post_install - } - - remove_module() { - if [[ -d "${PREFIX}" ]]; then - std::info \ - "%s " \ - "${module_name}/${module_version}:" \ - "removing all files in '${PREFIX}' ..." - [[ "${dry_run}" == 'no' ]] && rm -rf ${PREFIX} - fi - if [[ -e "${modulefile_name}" ]]; then - std::info \ - "%s " \ - "${module_name}/${module_version}:" \ - "removing modulefile '${modulefile_name}' ..." - [[ "${dry_run}" == 'no' ]] && rm -v "${modulefile_name}" - fi - local release_file="${modulefile_dir}/.release-${module_version}" - if [[ -e "${release_file}" ]]; then - std::info \ - "%s " \ - "${module_name}/${module_version}:" \ - "removing release file '${release_file}' ..." - [[ "${dry_run}" == 'no' ]] && rm -v "${release_file}" - fi - rmdir -p "${modulefile_dir}" 2>/dev/null || : - } - - ######################################################################## - # - # here we really start with make_all() - # - - # setup module specific environment - if [[ "${bootstrap}" == 'no' ]]; then - check_supported_systems - check_supported_os - check_supported_compilers - set_full_module_name_and_prefix - if [[ -e "${modulefile_name}" ]] \ - && [[ -d ${PREFIX} ]] \ - && [[ ${force_rebuild} != 'yes' ]]; then - if [[ "${module_release}" == 'removed' ]]; then - remove_module - else - std::info \ - "%s " \ - "${module_name}/${module_version}:" \ - ${with_modules:+with ${with_modules[@]}} \ - "already exists, not rebuilding ..." - if [[ "${opt_update_modulefiles}" == "yes" ]]; then - install_modulefile - fi - install_release_file - fi - else - if [[ "${module_release}" == 'deprecated' ]]; then - std::info \ - "%s " "${module_name}/${module_version}:" \ - ${with_modules:+with ${with_modules[@]}} \ - "is deprecated, skiping!" - install_release_file - else - build_module - fi - fi - else - build_module - fi - return 0 -} - -pbuild.init_env() { - #...................................................................... - # - # parse the passed version string - # - # the following global variables will be set in this function: - # V_MAJOR - # V_MINOR - # V_PATCHLVL - # V_RELEASE - # USE_FLAGS - # - parse_version() { - local v="$1" - V_MAJOR='' # first number in version string - V_MINOR='' # second number in version string (or empty) - V_PATCHLVL='' # third number in version string (or empty) - V_RELEASE='' # module release (or empty) - : ${USE_FLAGS:=''} # architectures (or empty) - - local tmp='' - - if [[ "$v" =~ "_" ]]; then - tmp="${v#*_}" - USE_FLAGS+=":${tmp//_/:}:" - v="${v%%_*}" - fi - V_PKG="${v%%-*}" # version without the release number - if [[ $v == *-* ]]; then - V_RELEASE="${v#*-}" # release number - else - V_RELEASE='' - fi - case "${V_PKG}" in - *.*.* ) - V_MAJOR="${V_PKG%%.*}" - tmp="${V_PKG#*.}" - V_MINOR="${tmp%%.*}" - V_PATCHLVL="${tmp#*.}" - ;; - *.* ) - V_MAJOR="${V_PKG%.*}" - V_MINOR="${V_PKG#*.}" - ;; - * ) - V_MAJOR="${V_PKG}" - ;; - esac - - VERSIONS=( ${V_MAJOR} ) - if [[ -n ${V_MINOR} ]]; then - VERSIONS=( ${V_MAJOR}.${V_MINOR} ${VERSIONS[@]} ) - fi - if [[ -n ${V_PATCHLVL} ]]; then - VERSIONS=( ${V_MAJOR}.${V_MINOR}.${V_PATCHLVL} ${VERSIONS[@]} ) - fi - if [[ -n ${V_RELEASE} ]]; then - VERSIONS=( ${V_MAJOR}.${V_MINOR}.${V_PATCHLVL}-${V_RELEASE} ${VERSIONS[@]} ) - fi - } - - local -r module_name="$1" - local -r module_version="$2" - - SRC_DIR="${PMODULES_TMPDIR}/${module_name}-${module_version}/src" - BUILD_DIR="${PMODULES_TMPDIR}/${module_name}-${module_version}/build" - - # P and V can be used in the build-script, so we have to set them here - P="${module_name}" - V="${module_version}" - parse_version "${module_version}" - - SOURCE_URLS=() - SOURCE_SHA256_SUMS=() - SOURCE_NAMES=() - CONFIGURE_ARGS=() - SUPPORTED_SYSTEMS=() - SUPPORTED_OS=() - SUPPORTED_COMPILERS=() - PATCH_FILES=() - PATCH_STRIPS=() - PATCH_STRIP_DEFAULT='1' - MODULE_DOCFILES=() - configure_with='undef' -} - pbuild.build_module() { - module_name="$1" - module_version="$2" - module_release="$3" + declare -gx module_name="$1" + declare -gx module_version="$2" + declare -gx module_release="$3" shift 3 with_modules=( "$@" ) - # used in pbuild::make_all - declare bootstrap='no' + # used in _make_all declare -a runtime_dependencies=() declare -a install_dependencies=() @@ -1286,66 +737,51 @@ pbuild.build_module() { # # test whether a module is loaded or not # - # $1: module name + # Arguments: + # $1 module name # is_loaded() { [[ :${LOADEDMODULES}: =~ :$1: ]] } - #...................................................................... + #...................................................................... # - # build a dependency + # Initialise environment modules. # - # $1: name of module to build + # Arguments: + # none # - # :FIXME: needs testing - # - build_dependency() { - local -r m=$1 - std::debug "${m}: module not available" - local rels=( ${ReleaseStages//:/ } ) - [[ ${dry_run} == yes ]] && \ - std::die 1 \ - "%s " \ - "${m}: module does not exist," \ - "cannot continue with dry run..." + init_module_environment(){ + eval $( "${MODULECMD}" bash use unstable ) + eval $( "${MODULECMD}" bash use deprecated ) + eval $( "${MODULECMD}" bash purge ) - std::info "%s " \ - "$m: module does not exist, trying to build it..." - local args=( '' ) - set -- ${ARGS[@]} - while (( $# > 0 )); do - case $1 in - -j ) - args+=( "-j $2" ) - shift - ;; - --jobs=[0-9]* ) - args+=( $1 ) - ;; - -v | --verbose) - args+=( $1 ) - ;; - --with=*/* ) - args+=( $1 ) - ;; - esac - shift - done - - find_build_script(){ - local p=$1 - local script=$(find "${BUILDBLOCK_DIR}/../.." -path "*/$p/build") - std::get_abspath "${script}" - } - local buildscript=$(find_build_script "${m%/*}") - [[ -x "${buildscript}" ]] || \ - std::die 1 \ - "$m: build-block not found!" - if ! "${buildscript}" "${m#*/}" ${args[@]}; then - std::die 1 \ - "$m: oops: build failed..." + # :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 ) fi + if [[ -e "${PMODULES_HOME%%/Tools*}/System" ]]; then + eval $( "${MODULECMD}" bash use System ) + fi + unset C_INCLUDE_PATH + unset CPLUS_INCLUDE_PATH + unset CPP_INCLUDE_PATH + unset LIBRARY_PATH + unset LD_LIBRARY_PATH + unset DYLD_LIBRARY_PATH + + unset CFLAGS + unset CPPFLAGS + unset CXXFLAGS + unset LIBS + unset LDFLAGS + + unset CC + unset CXX + unset FC + unset F77 + unset F90 } #...................................................................... @@ -1353,13 +789,71 @@ pbuild.build_module() { # Load build- and run-time dependencies. # # Arguments: - # none + # none # # Variables - # [r] module_release set if defined in a variants file - # runtime_dependencies runtime dependencies from variants added + # module_release set if defined in a variants file + # runtime_dependencies runtime dependencies from variants added # load_build_dependencies() { + + #.............................................................. + # + # build a dependency + # + # $1: name of module to build + # + # :FIXME: needs testing + # + build_dependency() { + find_build_script(){ + local p=$1 + local script=$(${find} "${BUILDBLOCK_DIR}/../.." \ + -path "*/$p/build") + std::get_abspath "${script}" + } + + local -r m=$1 + std::debug "${m}: module not available" + [[ ${dry_run} == yes ]] && \ + std::die 1 \ + "%s " \ + "${m}: module does not exist," \ + "cannot continue with dry run..." + + std::info "%s " \ + "$m: module does not exist, trying to build it..." + local args=( '' ) + set -- ${ARGS[@]} + while (( $# > 0 )); do + case $1 in + -j ) + args+=( "-j $2" ) + shift + ;; + --jobs=[0-9]* ) + args+=( $1 ) + ;; + -v | --verbose) + args+=( $1 ) + ;; + --with=*/* ) + args+=( $1 ) + ;; + esac + shift + done + + local buildscript=$(find_build_script "${m%/*}") + [[ -x "${buildscript}" ]] || \ + std::die 1 \ + "$m: build-block not found!" + if ! "${buildscript}" "${m#*/}" ${args[@]}; then + std::die 1 \ + "$m: oops: build failed..." + fi + } + local m='' for m in "${with_modules[@]}"; do @@ -1421,97 +915,675 @@ pbuild.build_module() { done } + init_build_environment() { + #...................................................................... + # + # parse the passed version string + # + # the following global variables will be set in this function: + # V_MAJOR + # V_MINOR + # V_PATCHLVL + # V_RELEASE + # USE_FLAGS + # + parse_version() { + local v="$1" + V_MAJOR='' # first number in version string + V_MINOR='' # second number in version string (or empty) + V_PATCHLVL='' # third number in version string (or empty) + V_RELEASE='' # module release (or empty) + : ${USE_FLAGS:=''} # architectures (or empty) + + local tmp='' + + if [[ "$v" =~ "_" ]]; then + tmp="${v#*_}" + USE_FLAGS+=":${tmp//_/:}:" + v="${v%%_*}" + fi + V_PKG="${v%%-*}" # version without the release number + if [[ $v == *-* ]]; then + V_RELEASE="${v#*-}" # release number + else + V_RELEASE='' + fi + case "${V_PKG}" in + *.*.* ) + V_MAJOR="${V_PKG%%.*}" + tmp="${V_PKG#*.}" + V_MINOR="${tmp%%.*}" + V_PATCHLVL="${tmp#*.}" + ;; + *.* ) + V_MAJOR="${V_PKG%.*}" + V_MINOR="${V_PKG#*.}" + ;; + * ) + V_MAJOR="${V_PKG}" + ;; + esac + + VERSIONS=() + if [[ -n ${V_RELEASE} ]]; then + VERSIONS+=( ${V_PKG}-${V_RELEASE} ) + fi + if [[ -n ${V_PATCHLVL} ]]; then + VERSIONS+=( ${V_MAJOR}.${V_MINOR}.${V_PATCHLVL} ) + fi + if [[ -n ${V_MINOR} ]]; then + VERSIONS+=( ${V_MAJOR}.${V_MINOR} ) + fi + VERSIONS+=( ${V_MAJOR} ) + } + + local -r module_name="$1" + local -r module_version="$2" + + SRC_DIR="${PMODULES_TMPDIR}/${module_name}-${module_version}/src" + BUILD_DIR="${PMODULES_TMPDIR}/${module_name}-${module_version}/build" + + # P and V can be used in the build-script, so we have to set them here + P="${module_name}" + V="${module_version}" + parse_version "${module_version}" + declare -gx GROUP='' + declare -g PREFIX='' + + SOURCE_URLS=() + SOURCE_SHA256_SUMS=() + SOURCE_NAMES=() + declare -Ag SOURCE_UNPACK_DIRS=() + CONFIGURE_ARGS=() + SUPPORTED_SYSTEMS=() + SUPPORTED_OS=() + SUPPORTED_COMPILERS=() + PATCH_FILES=() + PATCH_STRIPS=() + PATCH_STRIP_DEFAULT='1' + MODULE_DOCFILES=() + configure_with='undef' + } # init_build_environment() + + #...................................................................... + check_supported_systems() { + (( ${#SUPPORTED_SYSTEMS[@]} == 0 )) && return 0 + for sys in "${SUPPORTED_SYSTEMS[@]}"; do + [[ ${sys,,} == ${system,,} ]] && return 0 + done + std::die 1 \ + "%s " "${module_name}/${module_version}:" \ + "Not available for ${system}." + } + + #...................................................................... + check_supported_os() { + (( ${#SUPPORTED_OS[@]} == 0 )) && return 0 + for os in "${SUPPORTED_OS[@]}"; do + [[ ${os,,} == ${OS,,} ]] && return 0 + done + std::die 1 \ + "%s " "${module_name}/${module_version}:" \ + "Not available for ${OS}." + } + + #...................................................................... + check_supported_compilers() { + (( ${#SUPPORTED_COMPILERS[@]} == 0 )) && return 0 + for compiler in "${SUPPORTED_COMPILERS[@]}"; do + [[ ${compiler,,} == ${COMPILER,,} ]] && return 0 + done + std::die 1 \ + "%s " "${module_name}/${module_version}:" \ + "Not available for ${COMPILER}." + } + + #...................................................................... + # + # compute full module name and installation prefix + # + # The following variables are expected to be set: + # GROUP module group + # P module name + # V module version + # variables defining the hierarchical environment like + # COMPILER and COMPILER_VERSION + # + # The following variables are set in this function + # modulefile_dir + # modulefile_name + # PREFIX + # + set_full_module_name_and_prefix() { + do_simple_group(){ + modulefile_dir="${ol_mod_root}/${GROUP}/${PMODULES_MODULEFILES_DIR}/" + modulefile_dir+="${module_name}" + modulefile_name="${modulefile_dir}/${module_version}" + PREFIX="${ol_inst_root}/${GROUP}/${module_name}/${module_version}" + } + do_hierarchical_group(){ + join_by() { + local IFS="$1" + shift + echo "$*" + } + # define hierarchies + if [[ -v COMPILER_VERSION ]]; then + Compiler_HIERARCHY='${COMPILER}/${COMPILER_VERSION}' + else + unset Compiler_HIERARCHY + fi + if [[ -v COMPILER_VERSION ]] && \ + [[ -v HDF5_SERIAL_VERSION ]]; then + HDF5_serial_HIERARCHY='${COMPILER}/${COMPILER_VERSION}' + HDF5_serial_HIERARCHY+=' hdf5_serial/${HDF5_SERIAL_VERSION}' + else + unset HDF5_serial_HIERARCHY + fi + if [[ -v COMPILER_VERSION ]] && \ + [[ -v MPI_VERSION ]]; then + MPI_HIERARCHY='${COMPILER}/${COMPILER_VERSION}' + MPI_HIERARCHY+=' ${MPI}/${MPI_VERSION}' + else + unset MPI_HIERARCHY + fi + if [[ -v COMPILER_VERSION ]] && \ + [[ -v MPI_VERSION ]] && \ + [[ HDF5_VERSION ]]; then + HDF5_HIERARCHY='${COMPILER}/${COMPILER_VERSION}' + HDF5_HIERARCHY+=' ${MPI}/${MPI_VERSION}' + HDF5_HIERARCHY+=' hdf5/${HDF5_VERSION}' + else + unset HDF5_HIERARCHY + fi + + # evaluate + local names=() + local -n vname="${GROUP}"_HIERARCHY + if [[ -v vname ]]; then + names=( $(eval echo ${vname}) ) + else + std::die 1 \ + "%s: %s" \ + "${module_name}/${module_version}" \ + "not all hierarchical dependencies loaded!" + fi + + modulefile_dir=$(join_by '/' \ + "${ol_mod_root}" \ + "${GROUP}" \ + "${PMODULES_MODULEFILES_DIR}" \ + "${names[@]}" \ + "${module_name}") + modulefile_name="${modulefile_dir}/${module_version}" + + PREFIX="${ol_inst_root}/${GROUP}/${module_name}/${module_version}" + local -i i=0 + for ((i=${#names[@]}-1; i >= 0; i--)); do + PREFIX+="/${names[i]}" + done + } + + [[ -n ${GROUP} ]] || std::die 1 \ + "%s: %s" \ + "${module_name}/${module_version}" \ + "group not set." + + local -i grp_depth + compute_group_depth grp_depth "${ol_mod_root}/${GROUP}/${PMODULES_MODULEFILES_DIR}" + if (( grp_depth == 0 )); then + do_simple_group + else + do_hierarchical_group + fi + } # set_full_module_name_and_prefix + + #...................................................................... + # post-install. + # + # Arguments: + # none + post_install() { + #.............................................................. + # post-install: + # - build-script + # - list of loaded modules while building + # - doc-files specified in the build-script + # + # Arguments: + # none + # + install_doc() { + local -r docdir="${PREFIX}/${_DOCDIR}/${module_name}" + std::info \ + "%s " \ + "${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 | \ + ${grep} -v "Currently Loaded" > \ + "${docdir}/dependencies" || : + + if [[ ! -v MODULE_DOCFILES[0] ]]; then + # 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 + fi + if [[ ! -v MODULE_DOCFILES[0] ]]; then + return 0 + fi + ${install} -m0644 \ + "${MODULE_DOCFILES[@]/#/${SRC_DIR}/}" \ + "${docdir}" + return 0 + } + + #.............................................................. + # post-install: write file with required modules + write_runtime_dependencies() { + local -r fname="$1" + shift + std::info \ + "%s " \ + "${module_name}/${module_version}:" \ + "writing run-time dependencies to ${fname} ..." + local dep + echo -n "" > "${fname}" + for dep in "$@"; do + [[ -z $dep ]] && continue + if [[ ! $dep == */* ]]; then + # no version given: derive the version + # from the currently loaded module + dep=$( "${MODULECMD}" bash list -t 2>&1 1>/dev/null \ + | grep "^${dep}/" ) + fi + echo "${dep}" >> "${fname}" + done + } + + #.............................................................. + # post-install: for Linux we need a special post-install to + # solve the multilib problem with LIBRARY_PATH on 64-bit systems + post_install_linux() { + std::info \ + "%s " \ + "${module_name}/${module_version}:" \ + "running post-installation for ${OS} ..." + cd "${PREFIX}" + [[ -d "lib" ]] && [[ ! -d "lib64" ]] && ln -s lib lib64 + return 0 + } + + #.............................................................. + # post-install + cd "${BUILD_DIR}" + [[ "${OS}" == "Linux" ]] && post_install_linux + install_doc + if [[ -v runtime_dependencies[0] ]]; then + write_runtime_dependencies \ + "${PREFIX}/${FNAME_RDEPS}" \ + "${runtime_dependencies[@]}" + fi + if [[ -v install_dependencies[0] ]]; then + write_runtime_dependencies \ + "${PREFIX}/${FNAME_IDEPS}" \ + "${install_dependencies[@]}" + fi + install_modulefile + install_release_file + cleanup_build + cleanup_src + std::info \ + "%s " \ + "${module_name}/${module_version}:" \ + "Done ..." + return 0 + } # post_install + + #...................................................................... + # Install modulefile in ${ol_mod_root}/${GROUP}/modulefiles/... + # The modulefiles in the build-block can be + # versioned like + # modulefile-10.2.0 + # modulefile-10.2 + # modulefile-10 + # modulefile + # the most specific modulefile will be selected. Example: + # For a version 10.2.1 the file moduelfile-10.2 would be + # selected. + # + # Arguments + # none + # + # Used gloabal variables: + # VERSIONS + # BUILDBLOCK_DIR + # modulefile_name + # + install_modulefile() { + #.............................................................. + # Select the modulefile to install. + # + # Arguments: + # $1 upvar to return the filename + # + find_modulefile() { + local -n _modulefile="$1" + local fname='' + for fname in "${VERSIONS[@]/#/modulefile-}" 'modulefile'; do + if [[ -r "${BUILDBLOCK_DIR}/${fname}" ]]; then + _modulefile="${BUILDBLOCK_DIR}/${fname}" + break; + fi + done + [[ -n "${_modulefile}" ]] + } + + local src='' + find_modulefile src + if (( $? != 0 )); then + std::info \ + "%s " \ + "${module_name}/${module_version}:" \ + "skipping modulefile installation ..." + return + fi + std::info \ + "%s " \ + "${module_name}/${module_version}:" \ + "adding modulefile to overlay '${ol_name}' ..." + ${mkdir} -p "${modulefile_dir}" + ${install} -m 0644 "${src}" "${modulefile_name}" + } + + cleanup_modulefiles(){ + local ol='' + for ol in "${Overlays[@]}"; do + local i + for ((i=0; i<${#mod_overlays}; i++ )); do + [[ "${ol}" == "{mod_overlays[i]}" ]] && continue 2 + done + [[ "${ol}" == "${ol_name}" ]] && continue + local mod_root="${OverlayInfo[${ol}:mod_root]}" + local dir="${modulefile_dir/${ol_mod_root}/${mod_root}}" + local fname="${dir}/${module_version}" + if [[ -e "${fname}" ]]; then + std::info "%s "\ + "${module_name}/${module_version}:" \ + "removing modulefile from overlay '${ol}' ..." + ${rm} "${fname}" + fi + fname="${dir}/.release-${module_version}" + if [[ -e "${fname}" ]]; then + std::info \ + "%s " \ + "${module_name}/${module_version}:" \ + "removing release file from overlay '${ol}' ..." + ${rm} "${fname}" + fi + done + } + + install_release_file() { + local -r release_file="${modulefile_dir}/.release-${module_version}" + + if [[ -r "${release_file}" ]]; then + local release + read release < "${release_file}" + if [[ "${release}" != "${module_release}" ]]; then + std::info \ + "%s " \ + "${module_name}/${module_version}:" \ + "changing release from" \ + "'${release}' to '${module_release}' ..." + echo "${module_release}" > "${release_file}" + fi + else + std::info \ + "%s " \ + "${module_name}/${module_version}:" \ + "setting release to '${module_release}' ..." + echo "${module_release}" > "${release_file}" + fi + } + + cleanup_build() { + [[ ${enable_cleanup_build} == yes ]] || return 0 + [[ "${BUILD_DIR}" == "${SRC_DIR}" ]] && return 0 + { + cd "/${BUILD_DIR}/.." || std::die 42 "Internal error" + [[ "$(${pwd})" == "/" ]] && \ + std::die 1 \ + "%s " "${module_name}/${module_version}:" \ + "Oops: internal error:" \ + "BUILD_DIR is set to '/'" + + std::info \ + "%s " \ + "${module_name}/${module_version}:" \ + "Cleaning up '${BUILD_DIR}'..." + ${rm} -rf "${BUILD_DIR##*/}" + }; + return 0 + } + + cleanup_src() { + [[ ${enable_cleanup_src} == yes ]] || return 0 + { + cd "/${SRC_DIR}/.." || std::die 42 "Internal error" + [[ $(pwd) == / ]] && \ + std::die 1 \ + "%s " "${module_name}/${module_version}:" \ + "Oops: internal error:" \ + "SRC_DIR is set to '/'" + std::info \ + "%s " \ + "${module_name}/${module_version}:" \ + "Cleaning up '${SRC_DIR}'..." + rm -rf "${SRC_DIR##*/}" + }; + return 0 + } + + #...................................................................... + # build module ${module_name}/${module_version} + compile_and_install() { + build_target() { + local dir="$1" # src or build directory, depends on target + local target="$2" # prep, configure, compile or install + + if [[ -e "${BUILD_DIR}/.${target}" ]] && \ + [[ ${force_rebuild} != 'yes' ]]; then + 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::${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::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} ) + + for t in "${targets[@]}"; do + # We cd into the dir before calling the function - + # just to be sure we are in the right directory. + # + # Executing the function in a sub-process doesn't + # work because in some function global variables + # might/need to be set. + # + cd "${dir}" + if typeset -F "$t" 2>/dev/null; then + "$t" || \ + std::die 10 "Aborting..." + fi + done + touch "${BUILD_DIR}/.${target}" + } # compile_and_install():build_target() + + [[ ${dry_run} == yes ]] && std::die 0 "" + + ${mkdir} -p "${SRC_DIR}" + ${mkdir} -p "${BUILD_DIR}" + + std::info \ + "%s " \ + "${module_name}/${module_version}:" \ + "preparing sources ..." + build_target "${SRC_DIR}" prep + [[ "${build_target}" == "prep" ]] && return 0 + + std::info \ + "%s " \ + "${module_name}/${module_version}:" \ + "configuring ..." + build_target "${BUILD_DIR}" configure + [[ "${build_target}" == "configure" ]] && return 0 + + std::info \ + "%s " \ + "${module_name}/${module_version}:" \ + "compiling ..." + build_target "${BUILD_DIR}" compile + [[ "${build_target}" == "compile" ]] && return 0 + + ${mkdir} -p "${PREFIX}" + std::info \ + "%s " \ + "${module_name}/${module_version}:" \ + "installing ..." + build_target "${BUILD_DIR}" install + } # compile_and_install() + + remove_module() { + if [[ -d "${PREFIX}" ]]; then + std::info \ + "%s " \ + "${module_name}/${module_version}:" \ + "removing all files in '${PREFIX}' ..." + [[ "${dry_run}" == 'no' ]] && ${rm} -rf ${PREFIX} + fi + if [[ -e "${modulefile_name}" ]]; then + std::info \ + "%s " \ + "${module_name}/${module_version}:" \ + "removing modulefile '${modulefile_name}' ..." + [[ "${dry_run}" == 'no' ]] && ${rm} -v "${modulefile_name}" + fi + local release_file="${modulefile_dir}/.release-${module_version}" + if [[ -e "${release_file}" ]]; then + std::info \ + "%s " \ + "${module_name}/${module_version}:" \ + "removing release file '${release_file}' ..." + [[ "${dry_run}" == 'no' ]] && rm -v "${release_file}" + fi + ${rmdir} -p "${modulefile_dir}" 2>/dev/null || : + } + + deprecate_module(){ + std::info \ + "%s " \ + "${module_name}/${module_version}:" \ + "is deprecated, skiping!" + install_release_file + } + std::info \ "%s " \ "${module_name}/${module_version}:" \ ${with_modules:+with ${with_modules[@]}} \ "building ..." - MODULECMD="${PMODULES_HOME}/bin/modulecmd" - [[ -x ${MODULECMD} ]] || \ - std::die 2 "No such file or executable -- '${MODULECMD}'" - - 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 ) - fi - if [[ -e "${PMODULES_HOME%%/Tools*}/System" ]]; then - eval $( "${MODULECMD}" bash use System ) - fi - unset C_INCLUDE_PATH - unset CPLUS_INCLUDE_PATH - unset CPP_INCLUDE_PATH - unset LIBRARY_PATH - unset LD_LIBRARY_PATH - unset DYLD_LIBRARY_PATH - - unset CFLAGS - unset CPPFLAGS - unset CXXFLAGS - unset LIBS - unset LDFLAGS - - unset CC - unset CXX - unset FC - unset F77 - unset F90 + init_module_environment load_build_dependencies + init_build_environment "${module_name}" "${module_version}" - pbuild.init_env "${module_name}" "${module_version}" - pbuild::make_all + source "${BUILD_SCRIPT}" + + # module name including path in hierarchy and version + # (ex: 'gcc/6.1.0/openmpi/1.10.2' for openmpi compiled with gcc 6.1.0) + local modulefile_dir='' + local modulefile_name='' + + # + # :FIXME: add comments what and why we are doing this. + # + local -r logfile="${BUILDBLOCK_DIR}/pbuild.log" + rm -f "${logfile}" + if [[ "${verbose}" == 'yes' ]]; then + exec > >(${tee} -a "${logfile}") + else + exec > >(${cat} >> "${logfile}") + fi + exec 2> >(${tee} -a "${logfile}" >&2) + + # the group must have been defined - otherwise we cannot continue + [[ -n ${GROUP} ]] || \ + std::die 5 \ + "%s " "${module_name}/${module_version}:" \ + "Module group not set! Aborting ..." + + # check whether this module is supported + check_supported_systems + check_supported_os + check_supported_compilers + # setup module name and prefix + set_full_module_name_and_prefix + + # ok, finally we can start ... + std::info \ + "%s " \ + "${module_name}/${module_version}:" \ + ${with_modules:+build with ${with_modules[@]}} + + if [[ "${module_release}" == 'removed' ]]; then + remove_module + elif [[ "${module_release}" == 'deprecated' ]]; then + deprecate_module + elif [[ -d ${PREFIX} ]] && [[ ${force_rebuild} != 'yes' ]]; then + std::info \ + "%s " \ + "${module_name}/${module_version}:" \ + "already exists, not rebuilding ..." + if [[ "${opt_update_modulefiles}" == "yes" ]] || \ + [[ ! -e "${modulefile_name}" ]]; then + install_modulefile + fi + install_release_file + else + std::info \ + "%s " \ + "${module_name}/${module_version}:" \ + "start building ..." + compile_and_install + post_install + fi + cleanup_modulefiles std::info "* * * * *\n" } - -pbuild.bootstrap() { - local -r module_name="$1" - local -r module_version="$2" - - # used in pbuild::make_all - bootstrap='yes' - - pbuild.init_env "${module_name}" "${module_version}" - - MODULECMD=$(which true) - GROUP='Tools' - PREFIX="${ol_dir}/${GROUP}/Pmodules/${PMODULES_VERSION}" - - C_INCLUDE_PATH="${PREFIX}/include" - CPLUS_INCLUDE_PATH="${PREFIX}/include" - CPP_INCLUDE_PATH="${PREFIX}/include" - LIBRARY_PATH="${PREFIX}/lib" - LD_LIBRARY_PATH="${PREFIX}/lib" - DYLD_LIBRARY_PATH="${PREFIX}/lib" - - PATH+=":${PREFIX}/bin" - PATH+=":${PREFIX}/sbin" - pbuild::make_all -} - -#----------------------------------------------------------------------------- -# -read_config_file() { - local fname="$1" - if [[ ! -r "${fname}" ]]; then - std::die 1 "Configuration file '${fname}' does not exist or is not readable!" - fi - - eval $(std::parse_yaml "${fname}" '') || std::die 1 "Cannot read configuration file '${fname}'" - - PMODULES_ROOT="${Overlays_base_install_root}" - PMODULES_DISTFILESDIR="${DistfilesDir}" - PMODULES_TMPDIR="${TmpDir}" - PMODULES_HOME="${PMODULES_ROOT}/Tools/Pmodules/${PMODULES_VERSION}" - - if [[ -z "${PMODULES_HOME}" ]]; then - std::die 1 "Error in configuration file '${fname}': PMODULE_HOME not defined!" - fi -} +readonly -f pbuild.build_module # Local Variables: # mode: sh diff --git a/Pmodules/libpbuild_dyn.bash b/Pmodules/libpbuild_dyn.bash deleted file mode 100644 index 85f1d76..0000000 --- a/Pmodules/libpbuild_dyn.bash +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -eval "pbuild::pre_prep_${system}() { :; }" -eval "pbuild::pre_prep_${OS}() { :; }" -eval "pbuild::post_prep_${system}() { :; }" -eval "pbuild::post_prep_${OS}() { :; }" - -eval "pbuild::add_patch_${system}() { pbuild::add_patch \"\$@\"; }" -eval "pbuild::add_patch_${OD}() { pbuild::add_patch \"\$@\"; }" - -eval "pbuild::pre_configure_${system}() { :; }" -eval "pbuild::pre_configure_${OS}() { :; }" -eval "pbuild::post_configure_${system}() { :; }" -eval "pbuild::post_configure_${OS}() { :; }" - -eval "pbuild::pre_compile_${system}() { :; }" -eval "pbuild::pre_compile_${OS}() { :; }" -eval "pbuild::post_compile_${system}() { :; }" -eval "pbuild::post_compile_${OS}() { :; }" - -eval "pbuild::pre_install_${system}() { :; }" -eval "pbuild::pre_install_${OS}() { :; }" -eval "pbuild::post_install_${system}() { :; }" -eval "pbuild::post_install_${OS}() { :; }" diff --git a/Pmodules/libpmodules.bash.in b/Pmodules/libpmodules.bash.in index 8b6992b..30d2f25 100644 --- a/Pmodules/libpmodules.bash.in +++ b/Pmodules/libpmodules.bash.in @@ -8,9 +8,14 @@ declare -A Options=() declare -A Help=() declare -a Overlays=() -declare -A OverlayDict -declare -a OverlayList -declare -Ag Dir2OverlayMap +declare -A OverlayInfo +declare -a UsedOverlays +declare -A Dir2OverlayMap + +declare -r ol_normal='n' +declare -r ol_hiding='h' +declare -r ol_replacing='r' + # initialize help text of 'module --version' Help['version']=" @@ -33,33 +38,42 @@ print_help() { # $1: absolute path of a modulefile directory # compute_group_depth () { - local -r dir=$1 - test -d "${dir}" || return 1 + 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##*/} - [[ -n "${GroupDepths[${group}]}" ]] && return 0 - local -i depth=$(${find} "${dir}" -depth \( -type f -o -type l \) \ + result=$(${find} "${dir}" -depth \( -type f -o -type l \) \ -printf "%d" -quit 2>/dev/null) - (( depth-=2 )) + (( result-=2 )) # if a group doesn't contain a modulefile, depth is negativ # :FIXME: better solution? - (( depth < 0 )) && (( depth = 0 )) - GroupDepths[$group]=${depth} + (( result < 0 )) && (( result = 0 )) } # # (Re-)Scan available groups in given overlays and compute group depth's # # Args: -# $1: array of overlays +# $@: overlay names # scan_groups () { local ol + local depth for ol in "$@"; do - local mod_root="${OverlayDict[${ol}:mod_root]}" - local moduledir - for moduledir in ${mod_root}/*/${PMODULES_MODULEFILES_DIR}; do - compute_group_depth "${moduledir}" + local mod_root="${OverlayInfo[${ol}:mod_root]}" + local dir + for dir in ${mod_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 } @@ -73,17 +87,24 @@ pm::read_config(){ # # Args: # $1 [upvar] result + local -n fnames="$1" + + # 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" \ "$_" - config_files+=("${PMODULES_OVERLAYS_DEF}") + fnames+=("${PMODULES_OVERLAYS_DEF}") fi + + # user defined if [[ -r "${HOME}/.Pmodules/Pmodules.yaml" ]]; then - config_files+=("${HOME}/.Pmodules/Pmodules.yaml") + fnames+=("${HOME}/.Pmodules/Pmodules.yaml") fi + + # system config file test -r "${PMODULES_HOME%%/Tools*}/config/Pmodules.yaml" || \ std::die 3 \ "%s %s -- %s" \ @@ -91,14 +112,19 @@ pm::read_config(){ "does not exist or is not readable" \ "$_" - config_files+=("${PMODULES_HOME%%/Tools*}/config/Pmodules.yaml") + fnames+=("${PMODULES_HOME%%/Tools*}/config/Pmodules.yaml") } _get_ol_names(){ - ${yq} -Ne eval-all '. as $item ireduce ({}; . *+ $item) |.Overlays|keys()' "${config_files[@]}" | awk '{print $2}' + # + # 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_config_files + _get_config_files config_files eval $(std::parse_yaml "${config_files[-1]}" 'cfg_') [[ -v cfg_DefaultGroups ]] && DefaultGroups="${cfg_DefaultGroups}" @@ -106,14 +132,37 @@ pm::read_config(){ [[ -v cfg_ReleaseStages ]] && ReleaseStages="${cfg_ReleaseStages}" unset ${!cfg_*} - Overlays=( $(_get_ol_names) ) + # + # 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='' for ol in "${Overlays[@]}"; do eval $(${yq} -Ne eval-all \ ". as \$item ireduce ({}; . *+ \$item) |.Overlays.${ol}" \ "${config_files[@]}" | \ sed 's/: /=/; s/\(.*\)/local \1/') - : ${type:=n} + + [[ -n ${install_root} ]] || \ + std::die 3 \ + "install_root missing for overlay -- ${ol}" + [[ -d ${install_root} ]] || \ + std::die 3 \ + "Invalid installation root directory for overlay '${ol}' -- ${install_root}" + OverlayInfo[${ol}:inst_root]="${install_root}" + + : ${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:=${ol_normal}} case ${type} in ${ol_normal} | ${ol_replacing} | ${ol_hiding} ) : @@ -122,23 +171,9 @@ pm::read_config(){ std::die 3 "Invalid type for overlay '${ol}' -- ${type}" ;; esac - OverlayDict[${ol}:type]="${type}" + OverlayInfo[${ol}:type]="${type}" - : ${modulefiles_root:=${PMODULES_HOME%%/Tools*}} - [[ -d ${modulefiles_root} ]] || \ - std::die 3 \ - "Invalid modulefiles root directory for overlay '${ol}' -- ${modulefiles_root}" - : ${modulefiles_root:=${PMODULES_HOME%%/Tools*}} - OverlayDict[${ol}:mod_root]="${modulefiles_root}" - Dir2OverlayMap[${modulefiles_root}]="${ol}" - - : ${install_root:=${PMODULES_HOME%%/Tools*}} - [[ -d ${install_root} ]] || \ - std::die 3 \ - "Invalid installation root directory for overlay '${ol}' -- ${install_root}" - OverlayDict[${ol}:inst_root]="${install_root:-${PMODULES_HOME%%/Tools*}}" - - OverlayDict[${ol}:used]='no' + OverlayInfo[${ol}:used]='no' unset type modulefiles_root install_root done } diff --git a/Pmodules/libstd.bash b/Pmodules/libstd.bash index b39cced..eb3b6ba 100644 --- a/Pmodules/libstd.bash +++ b/Pmodules/libstd.bash @@ -20,7 +20,7 @@ std::error() { } std::debug() { - [[ ${PMODULES_DEBUG} ]] || return 0 + [[ -v PMODULES_DEBUG ]] || return 0 std::log 2 "$@" } @@ -39,7 +39,7 @@ std::def_cmds(){ local path="$1" shift for cmd in "$@"; do - eval declare -g ${cmd}=$(PATH="${path}" which $cmd 2>/dev/null) + eval declare -gr ${cmd}=$(PATH="${path}" /usr/bin/which $cmd 2>/dev/null) if [[ -z "${!cmd}" ]]; then std::die 255 "${cmd} not found" fi @@ -78,42 +78,42 @@ std::get_abspath() { } std::append_path () { - local -r P=$1 - local -r d=$2 + local -nr P="$1" + shift 1 + local dir + local dirs='' + for dir in "$@"; do + [[ "${P}" == @(|*:)${dir}@(|:*) ]] && continue + dirs+=":${dir}" + done - if ! echo ${!P} | egrep -q "(^|:)${d}($|:)" ; then - if [[ -z ${!P} ]]; then - export "$P=${d}" - else - export "$P=${!P}:${d}" - fi - fi + if [[ -z ${P} ]]; then + P="${dirs:1}" # remove leading ':' + else + P="${P}${dirs}" + fi } std::prepend_path () { - local -r P="$1" + local -nr P="$1" shift 1 - local new_path="$1" - shift 1 - local dir - if [[ -z ${!P} ]]; then - for dir in "$@"; do - new_path+=":${dir}" - done - export "$P=${new_path}" + local dir + local dirs='' + for dir in "$@"; do + [[ "${P}" == @(|*:)${dir}@(|:*) ]] && continue + dirs+="${dir}:" + done + + if [[ -z ${P} ]]; then + P="${dirs:0:-1}" # remove trailing ':' else - for dir in "$@"; do - [[ "${!P}" == @(|*:)${dir}@(|:*) ]] && continue - new_path+=":${dir}" - done - new_path+=":${!P}" + P="${dirs}${P}" fi - std::upvar "$P" "${new_path}" } std::remove_path() { - local -r P=$1 + local -nr P="$1" shift 1 local -ar dirs="$@" local new_path='' @@ -125,8 +125,7 @@ std::remove_path() { [[ "${entry}" != "${dir}" ]] && new_path+=":${entry}" done done - # remove leading ':' - std::upvar "$P" "${new_path:1}" + P="${new_path:1}" # remove leading ':' } # @@ -176,22 +175,23 @@ std::replace_path () { # analog to std::split_abspath() with a relative path. # std::split_path() { - local parts="$1" - local -r path="$2" + local -n parts="$1" + local -r path="$2" IFS='/' local std__split_path_result=( ${std__split_path_tmp} ) unset IFS - std::upvar ${parts} "${std__split_path_result[@]}" + parts="${std__split_path_result[@]}" if (( $# >= 3 )); then # return number of parts - std::upvar "$3" ${#std__split_path_result[@]} + local -n num="$3" + num="${#std__split_path_result[@]}" fi } std::split_abspath() { - local parts="$1" - local -r path="$2" + local -n parts="$1" + local -r path="$2" if [[ "${path:0:1}" == '/' ]]; then local -r std__split_path_tmp="${path:1}" else @@ -201,16 +201,17 @@ std::split_abspath() { IFS='/' local std__split_path_result=( ${std__split_path_tmp} ) unset IFS - std::upvar ${parts} "${std__split_path_result[@]}" + parts="${std__split_path_result[@]}" if (( $# >= 3 )); then # return number of parts - std::upvar "$3" ${#std__split_path_result[@]} + local -n num="$3" + num="${#std__split_path_result[@]}" fi } std::split_relpath() { - local parts="$1" - local -r path="$2" + 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 @@ -220,10 +221,11 @@ std::split_relpath() { IFS='/' local std__split_path_result=( ${std__split_path_tmp} ) unset IFS - std::upvar ${parts} "${std__split_path_result[@]}" + parts="${std__split_path_result[@]}" if (( $# >= 3 )); then # return number of parts - std::upvar "$3" ${#std__split_path_result[@]} + local -n num="$3" + num="${#std__split_path_result[@]}" fi } @@ -287,11 +289,11 @@ std::upvar() { } std.get_os_release_linux() { - local lsb_release=$(which lsb_release) + #local lsb_release=$(which lsb_release) local ID='' local VERSION_ID='' - if [[ -n $(which lsb_release) ]]; then + if [[ -n $(which lsb_release 2>/dev/null) ]]; then ID=$(lsb_release -is) VERSION_ID=$(lsb_release -rs) elif [[ -r '/etc/os-release' ]]; then @@ -322,7 +324,7 @@ std::get_os_release() { local -A func_map; func_map['Linux']=std.get_os_release_linux func_map['Darwin']=std.get_os_release_macos - ${func_map[${OS}]} + ${func_map[$(uname -s)]} } std::get_type() { diff --git a/Pmodules/modbuild.in b/Pmodules/modbuild.in index 78a600e..6620c9b 100755 --- a/Pmodules/modbuild.in +++ b/Pmodules/modbuild.in @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!@BASH@ # # The following build specific variables are set and used in libpbuild.bash: # ARGS @@ -7,45 +7,59 @@ # #............................................................................. +set -x declare VERSION='@PMODULES_VERSION@' # get absolute path of script declare mydir=$(dirname "$0") declare -r mydir=$(cd ${mydir} && pwd -P) -# initialize PATH, -# add library installation directories to the PATH, -# so 'source' is able find them - -if [[ $(uname -s) == 'Darwin' ]]; then - PATH='/opt/local/bin:' -else - PATH='' -fi -PATH+='/usr/bin:/bin:/usr/sbin:/sbin' -PATH+=":${mydir}" -PATH+=":${mydir}/../lib:${mydir}/../config" -PATH+=":${PMODULES_DIR}/libexec" - -source libstd.bash || { +source "${mydir}/../lib/libstd.bash" || { echo "Oops: cannot source library -- '$_'" 1>&2; exit 3; } -# can be set in the configuration file -declare PMODULES_DISTFILESDIR='' -declare PMODULES_TMPDIR='' -declare pm_root="${PMODULES_HOME%%/Tools*}" +############################################################################## +# +# 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!" -source libpbuild.bash || \ +std::def_cmds "${mydir}/../libexec" \ + 'yq' + +std::def_cmds '/usr/bin:/bin:/usr/sbin:/sbin' \ + 'awk' 'base64' 'cat' 'cp' 'find' 'getopt' 'grep' \ + 'install' 'logger' 'make' 'mkdir' 'mktemp' 'patch' 'pwd' \ + 'rm' 'rmdir' '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='/usr/bin:/bin:/usr/sbin:/sbin' + +############################################################################## +source "${mydir}/../lib/libpbuild.bash" || \ std::die 3 "Oops: cannot source library -- '$_'" -source libpmodules.bash || \ +source "${mydir}/../lib/libpmodules.bash" || \ std::die 3 "Oops: cannot source library -- '$_'" -# save arguments, (still) required for building dependencies -declare -r ARGS="$@" - -shopt -s nocaseglob -shopt -s extglob +############################################################################## +set -o nounset +shopt -s nocaseglob +shopt -s extglob +shopt -s nullglob ############################################################################## # @@ -145,10 +159,13 @@ MISCELLANEOUS OPTIONS: # then configuration file # last default +# save arguments, required for building dependencies +declare -r ARGS="$@" +declare PMODULES_DISTFILESDIR='' +declare PMODULES_TMPDIR='' + # versions to be build, '.*' or none means all declare -a versions=() -declare opt_bootstrap='no' -declare opt_build_config='Pmodules.yaml' declare opt_build_target='all' declare opt_dry_run='no' declare opt_enable_cleanup_build='yes' @@ -157,12 +174,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=() -declare opt_ol_name_or_dir='' - +declare BUILD_SCRIPT='' parse_args() { while (( $# > 0 )); do @@ -193,13 +210,6 @@ parse_args() { --dry-run ) opt_dry_run='yes' ;; - --config ) - opt_build_config="$2" - shift 1 - ;; - --config=* ) - opt_build_config="${1#*=}" - ;; --enable-cleanup ) opt_enable_cleanup_build='yes' opt_enable_cleanup_src='yes' @@ -220,19 +230,21 @@ parse_args() { --disable-cleanup-src ) opt_enable_cleanup_src='no' ;; - --distdir ) - PMODULES_DISTFILESDIR="$2" - shift + --distdir | --distdir=* ) + if [[ $1 == *=* ]]; then + PMODULES_DISTFILESDIR="${1/--distdir=}" + else + PMODULES_DISTFILESDIR="$2" + shift + fi ;; - --distdir=* ) - PMODULES_DISTFILESDIR="${1/--distdir=}" - ;; - --tmpdir ) - PMODULES_TMPDIR="$2" - shift - ;; - --tmpdir=* ) - PMODULES_TMPDIR="${1/--tmpdir=}" + --tmpdir | --tmpdir=* ) + if [[ $1 == *=* ]]; then + PMODULES_TMPDIR="${1#--*=}" + else + PMODULES_TMPDIR="$2" + shift + fi ;; --system | --system=* ) if [[ $1 == *=* ]]; then @@ -250,27 +262,25 @@ parse_args() { shift fi ;; - --use-flags ) - USE_FLAGS="y:$2:" - shift + --use-flags | --use-flags=* ) + if [[ $1 == *=* ]]; then + USE_FLAGS=":${1#--*=}:" + else + USE_FLAGS=":$2:" + shift + fi ;; - --use-flags=* ) - USE_FLAGS=":${1/--use-flags=}:" - ;; - --with ) - opt_with_modules+=( "$2" ) - shift - ;; - --with=*/* ) - m="${1/--with=}" - opt_with_modules+=( ${m} ) + --with | --with=*/* ) + if [[ $1 == *=* ]]; then + opt_with_modules+=( "${1#--*=}" ) + else + opt_with_modules+=( "$2" ) + shift + fi ;; --prep | --configure | --compile | --install | --all ) opt_build_target=${1:2} ;; - --bootstrap ) - opt_bootstrap='yes' - ;; --update-modulefiles ) opt_update_modulefiles='yes' ;; @@ -301,7 +311,17 @@ parse_args() { esac shift done - [[ -n ${BUILD_SCRIPT} ]] || std::die 1 "No build-block specified!" + if [[ -z ${BUILD_SCRIPT} ]]; then + if [[ -r "${PWD}/build" ]]; then + if grep -q '#!.* modbuild' "${PWD}/build"; then + BUILD_SCRIPT="${PWD}/build" + BUILDBLOCK_DIR=$(dirname "${BUILD_SCRIPT}") + fi + fi + if [[ -z ${BUILD_SCRIPT} ]]; then + std::die 1 "Don't know what to build!" + fi + fi (( ${#versions[@]} > 0)) || versions+=( '.*' ) } @@ -317,7 +337,7 @@ parse_args() { # "text gcc/10.3.0 openmpi/4.0.5" # "text gcc/10.3.0 openmpi/4.1.0" # -brace_expand_with_prefix(){ +bash_expand(){ local text="$1" shift local to_expand=( "${@}" ) @@ -328,20 +348,13 @@ brace_expand_with_prefix(){ eval list=( ${to_expand[0]} ) local s for s in ${list[*]}; do - brace_expand_with_prefix "${text} ${s}" "${to_expand[@]:1}" + bash_expand "${text} ${s}" "${to_expand[@]:1}" done; fi; } -brace_expand(){ - brace_expand_with_prefix "" "$@" -} - build_modules_legacy() { find_variants_files(){ - shopt -q nullglob || : - local -i nullglob_set=$? - shopt -s nullglob local files=( "${BUILDBLOCK_DIR}"/*/"${BNAME_VARIANTS}"\.${opt_system} ) files+=( "${BUILDBLOCK_DIR}"/*/"${BNAME_VARIANTS}.$(uname -s)" ) local f @@ -350,7 +363,6 @@ build_modules_legacy() { || [[ -e "${f}.$(uname -s)" ]] \ || files+=( "$f" ) done - (( nullglob_set == 1 )) && shopt -u nullglob std::upvar "$1" "${files[@]}" } @@ -361,7 +373,7 @@ build_modules_legacy() { [[ -z ${toks} ]] && continue [[ ${toke:0:1} == '#' ]] && continue local -a deps=( ${toks[*]:2} ) - brace_expand_with_prefix "${toks[0]} ${toks[1]}" "${deps[@]}" + bash_expand "${toks[0]} ${toks[1]}" "${deps[@]}" done < "${input}" } @@ -384,7 +396,7 @@ build_modules_legacy() { local m local pattern="/^${name}\/${version}[[:blank:]]/" for m in "${with_modules[@]}"; do - if [[ -n $(awk "/${m%/*}[\/ ]/" "${files[@]}") ]]; then + if [[ -n $(${awk} "/${m%/*}[\/ ]/" "${files[@]}") ]]; then pattern+=" && /${m//\//\\/}/" fi done @@ -394,7 +406,7 @@ build_modules_legacy() { local line='' while read line; do variants+=( "${line}" ) - done < <(expand_variants_file "${f}" | awk "${pattern}") + done < <(expand_variants_file "${f}" | ${awk} "${pattern}") done if (( ${#variants[@]} == 0 )); then std::info "%s " \ @@ -410,9 +422,9 @@ build_modules_legacy() { std::die 10 "Aborting..." fi declare ol_name='base' - declare ol_type='' - declare ol_dir="${pm_root}" - declare ol_install_dir="${pm_root}" + declare ol_type='n' + declare ol_mod_root="${PMODULES_HOME%%/Tools*}" + declare ol_inst_root="${PMODULES_HOME%%/Tools*}" local -i i=0 local -i num_variants=${#variants[@]} for ((i = 0; i < num_variants; i++)); do @@ -428,106 +440,195 @@ build_modules_legacy() { } build_modules_yaml(){ - local variants_file="${BUILDBLOCK_DIR}/files/" - variants_file+="${BNAME_VARIANTS}.${opt_system}.yaml" + local -a fnames=() + local -A mod_overlays=() - # - # $1 file name - # $2 module name - # $3 index - # - yaml_get_overlay(){ - local ol=$(yq e ".$2.variants[$3].overlay" "$1") - if [[ "${ol}" != 'null' ]]; then - echo "${ol}" - return - fi - ol=$(yq e ".$2.overlay" "$1") - if [[ "${ol}" != 'null' ]]; then - echo "${ol}" - return - fi - echo 'base' - } + fnames+=( "${BUILDBLOCK_DIR}/files/${BNAME_VARIANTS}.${opt_system}.yaml" ) + fnames+=( "${BUILDBLOCK_DIR}/files/${BNAME_VARIANTS}.yaml" ) + fnames+=( '__zzzzz__' ) + for fname in "${fnames[@]}"; do + [[ -r "${fname}" ]] && break + done + [[ ${fname} == '__zzzzz__' ]] && \ + std::die 3 "No suitable YAML variants file found" + echo "Using ${fname}..." yaml_get_versions(){ - yq e ".$2.variants[].version" "$1" + local -n _result="$1" + local fname="$2" + local version="$3" + _result=( $(${yq} -Ne e \ + "with_entries(select(.key | test(\"^${version}\$\")))|keys" \ + "${fname}" 2>/dev/null | ${awk} '{print $2}') ) } yaml_get_num_variants(){ - yq e ".$2.variants | length" "$1" - } - - yaml_get_version(){ - yq e ".$2.variants[$3].version" "$1" - } - - yaml_get_dependencies(){ - yq e ".$2.variants[$2].dependencies[]" "$1" + local -n _result="$1" + local fname="$2" + local version="$3" + _result=$(${yq} -Ne e ".\"${version}\"|length" \ + "${fname}" 2>/dev/null) + if (( $? != 0 )); then + _result=0 + fi } yaml_get_relstage(){ - local relstage=$(yq e ".$2.variants[$3].relstage" "$1") - if [[ "${relstage}" != 'null' ]]; then - echo "${relstage}" - return - fi - relstage=$(yq e ".$2.relstage" "$1") - if [[ "${relstage}" != 'null' ]]; then - echo "${relstage}" - return - fi - echo 'unstable' + local -n _result="$1" + local fname="$2" + local version="$3" + local idx="$4" + _result=$(${yq} -Ne e ".\"${version}\"[${idx}].relstage" \ + "${fname}" 2>/dev/null) + (( $? == 0 )) && return + _result=$(${yq} -Ne e ".relstage" "${fname}" 2>/dev/null) + (( $? == 0 )) && return + _result='unstable' } - local name="$1" + yaml_get_overlay(){ + local -n _result="$1" + local fname="$2" + local version="$3" + local idx="$4" + _result=$(${yq} -Ne e ".\"${version}\"[${idx}].overlay" \ + "${fname}" 2>/dev/null) + (( $? == 0 )) && return + _result=$(${yq} -Ne e ".overlay" "${fname}" 2>/dev/null) + (( $? == 0 )) && return + _result='base' + } + + yaml_get_systems(){ + local -n _result="$1" + local fname="$2" + local version="$3" + local idx="$4" + _result=$(${yq} -Ne e ".\"${version}\"[${idx}].systems" \ + "${fname}" 2>/dev/null) + (( $? == 0 )) && return + _result=$(${yq} -Ne e ".systems" "${fname}" 2>/dev/null) + (( $? == 0 )) && return + _result='any' + } + + yaml_get_dependencies(){ + local -n _result="$1" + local fname="$2" + local version="$3" + local idx="$4" + _result=( $(${yq} -Ne e ".\"${version}\"[${idx}]|(.with, .dependencies)" \ + "${fname}" 2>/dev/null) ) + if (( $? != 0 )); then + # neither .with nor .dependencies are set + _result=() + return + fi + # if one of .with, .dependencies is not set, the vaulue is + # returned as 'null'. + local -i i + for i in "${!_result[@]}"; do + [[ ${_result[$i]} == 'null' ]] && unset -v '_result[$i]' + done + _result=("${_result[@]}") + } + + yaml_get_build_requirements(){ + local -n _result="$1" + local fname="$2" + local version="$3" + local idx="$4" + _result=( $(${yq} -Ne e ".\"${version}\"[${idx}].build_requires" \ + "${fname}" 2>/dev/null) ) + if (( $? != 0 )); then + _result=() + return + fi + } + + local name="$1" local version="$2" shift 2 local with_modules=( $* ) - # pattern used in the for-loop to filter dependencies via awk + local m local pattern="//" for m in "${with_modules[@]}"; do - pattern+=" && /${m//\//\\/}( |$)/" + pattern+=" && /${m//\//\\/}/" done - local -i n_variants=$(yaml_get_num_variants \ - "${variants_file}" "${name}") - local i - for (( i=0; i 0 )); then - with_modules=( $(brace_expand ${deps[@]} | awk "${pattern}") ) - (( ${#with_modules[@]} == 0 )) && continue - fi - local relstage=$(yaml_get_relstage \ - "${variants_file}" \ - "${name}" \ - "$i") - local ol=$(yaml_get_overlay \ - "${variants_file}" \ - "${name}" \ - "$i") - get_ol_info "${ol}" \ - ol_name \ - ol_type \ - ol_install_dir \ - ol_dir \ - || std::die 3 "${variants_file##*/}: unknown overlay -- ${ol_name_or_dir}" - pbuild.build_module \ - "${name}" "${version}" \ - "${relstage}" "${with_modules[@]}" + local -a versions + yaml_get_versions versions "${fname}" "${name}/${version}" + for v in "${versions[@]}"; do + local -i n_variants + yaml_get_num_variants n_variants "${fname}" "${v}" + (( n_variants == 0 )) && (( n_variants = 1 )) + local -i i + local -a deps=() + local -a build_requires=() + local relstage + local ol_name + for (( i=0; i 0 )); then + while read -a with_modules; do + pbuild.build_module \ + "${name}" "${v##*/}" \ + "${relstage}" "${with_modules[@]}" + done < <(bash_expand "" ${deps[@]}|${awk} "${pattern}") + else + pbuild.build_module \ + "${name}" "${v##*/}" \ + "${relstage}" + fi + done done } + build_modules() { - local variants_file="${BUILDBLOCK_DIR}/files/${BNAME_VARIANTS}.${opt_system}" - if [[ -e "${variants_file}.yaml" ]]; then + if [[ -n $(ls "${BUILDBLOCK_DIR}/files/${BNAME_VARIANTS}"*.yaml 2>/dev/null) ]]; then build_modules_yaml "$@" else build_modules_legacy "$@" @@ -554,18 +655,14 @@ pbuild.verbose "${opt_verbose}" # # read configuration for modbuild # -if [[ "${opt_bootstrap}" == 'yes' ]]; then - test -d "${BUILDBLOCK_DIR}/../../config" && PATH+=":$_" -fi +pm::read_config -read_config_file "${pm_root}/${PMODULES_CONFIG_DIR}/${opt_build_config}" - -# :FIXME: should go dist files to +# :FIXME: should dist files go to # ${pm_root}/var/distfiles # or # ${overlay}/var/distfiles # ? -: ${PMODULES_DISTFILESDIR:=${pm_root}/var/distfiles} +: ${PMODULES_DISTFILESDIR:="${PMODULES_HOME%%/Tools*}/var/distfiles"} : ${PMODULES_TMPDIR:=/var/tmp/${USER}} declare -r BUILD_SCRIPT @@ -575,21 +672,6 @@ declare -r BUILDBLOCK_DIR IFS=/ read -r -a fname <<< "${BUILD_SCRIPT:1}" module_name=${fname[${#fname[@]}-2]} -# -# are we bootstrapping? If yes, go for it... -# -if [[ "${opt_bootstrap}" == 'yes' ]]; then - declare ol_name='base' - declare ol_type='' - declare ol_dir="${pm_root}" - declare ol_install_dir="${pm_root}" - pbuild.bootstrap "${module_name}" "${versions[0]}" 'stable' - exit $? -fi - -# -# else -# for version in "${versions[@]}"; do build_modules "${module_name}" "${version}" "${opt_with_modules[@]}" done diff --git a/Pmodules/modulecmd.bash.in b/Pmodules/modulecmd.bash.in index aa93c93..b4a2035 100644 --- a/Pmodules/modulecmd.bash.in +++ b/Pmodules/modulecmd.bash.in @@ -27,11 +27,10 @@ source "${libdir}/libpmodules.bash" declare -r os_name="$(uname -s)" -path="/bin:/usr/bin" -[[ ${os_name} == 'Darwin' ]] && path="${libexecdir}:${path}" +path="${libexecdir}:/bin:/usr/bin" std::def_cmds "${path}" \ 'awk' 'base64' 'find' 'getopt' 'logger' 'mktemp' \ - 'rm' 'sort' 'find' 'yq' + 'rm' 'sort' 'yq' if [[ ${PMODULES_PURETCL} == yes ]]; then declare -r modulecmd="${libexecdir}/modulecmd.tcl" @@ -44,10 +43,6 @@ else declare -r modulecmd="${libexecdir}/modulecmd.bin" fi -declare -r ol_normal='n' -declare -r ol_hiding='h' -declare -r ol_replacing='r' - declare verbosity_lvl=${PMODULES_VERBOSITY:-'verbose'} declare Shell='' @@ -131,16 +126,16 @@ save_env() { vars+=( ReleaseStages ) vars+=( GroupDepths ) vars+=( Overlays ) - vars+=( OverlayList ) - vars+=( OverlayDict Dir2OverlayMap) + vars+=( UsedOverlays ) + vars+=( OverlayInfo Dir2OverlayMap) local s=$(typeset -p ${vars[@]}) - declare -g PMODULES_ENV=$( encode_base64 "$s" ) - export_env 'PMODULES_ENV' + declare -gx PMODULES_ENV=$( encode_base64 "$s" ) } _exit() { save_env "${g_env_must_be_saved}" + export_env 'PMODULES_ENV' if [[ -n "${tmpfile}" ]] && [[ -e "${tmpfile}" ]]; then ${rm} -f "${tmpfile}" || : fi @@ -148,34 +143,11 @@ _exit() { trap '_exit' EXIT -# -# map a moduledir to an overlay -# -# Args: -# $1 upvar for overlay -# $2 moduledir -# -map_moduledir2ol_name() { - local "$1" - local moduledir="${2//+(\/)/\/}" # replace multpile '/' with one - moduledir="${moduledir/%\/}" # remove trailing slash if exist - - if [[ ! -v Dir2OverlayMap[${moduledir}] ]]; then - local ol - for ol in "${OverlayList[@]}" 'other'; do - local mod_root="${OverlayDict[${ol}:mod_root]}" - [[ ${moduledir} == ${mod_root}/* ]] && break - done - Dir2OverlayMap[${moduledir}]="${ol}" - fi - std::upvar $1 "${Dir2OverlayMap[${moduledir}]}" -} - # # get release stage of module # Note: -# - the release stage of a module outside ${OverlayDict[@]} is always 'stable' -# - the release stage of a module inside ${OverlayDict[@]} without a +# - the release stage of a module outside ${OverlayInfo[@]} is always 'stable' +# - the release stage of a module inside ${OverlayInfo[@]} without a # coresponding file is always 'unstable' # # Args: @@ -185,13 +157,10 @@ map_moduledir2ol_name() { # get_release_stage() { local "$1" - local -r moduledir=$2 - local -r modulefile="$2/$3" + local -r dir="$2" + local -r modulefile="${dir}/$3" - local ol_name - map_moduledir2ol_name ol_name "${moduledir}" - - if [[ "${ol_name}" == 'other' ]]; then + if [[ ! -v Dir2OverlayMap[${dir%/${PMODULES_MODULEFILES_DIR}*}] ]]; then std::upvar $1 'stable' return fi @@ -228,15 +197,14 @@ find_overlay () { local "$2" local path="${3//+(\/)/\/}" # replace multpile '/' with one path="${path/%\/}" # remove trailing slash if exist + path="${path%/${PMODULES_MODULEFILES_DIR}*}" + + [[ -v Dir2OverlayMap[${path}] ]] || return 1 local ol="${Dir2OverlayMap[${path}]}" - map_moduledir2ol_name ol "${path}" std::upvar $1 "${ol}" - [[ "${ol}" == 'other' ]] && return 1 - - local group="${path#${OverlayDict[${ol}:mod_root]}/}" - group=${group%%/*} + local group="${path#${OverlayInfo[${ol}:mod_root]}/}" std::upvar $2 "${group}" return 0 } @@ -501,8 +469,8 @@ subcommand_load() { local "$1" local -r group="$2/${PMODULES_MODULEFILES_DIR}" local ol - for ol in "${OverlayList[@]}"; do - local inst_root="${OverlayDict[${ol}:inst_root]}" + for ol in "${UsedOverlays[@]}"; do + local inst_root="${OverlayInfo[${ol}:inst_root]}" if [[ -d "${inst_root}/${group}" ]]; then std::upvar $1 "${ol}" return 0 @@ -515,8 +483,10 @@ subcommand_load() { [[ -n ${group} ]] && [[ -n ${GroupDepths[${group}]} ]] && return 0 local ol='' find_overlay_with_group ol "${group}" || return 1 - local moduledir="${OverlayDict[${ol}:mod_root]}/${group}/${PMODULES_MODULEFILES_DIR}" - compute_group_depth "${moduledir}" || return 1 + local moduledir="${OverlayInfo[${ol}:mod_root]}/${group}/${PMODULES_MODULEFILES_DIR}" + local -i depth + compute_group_depth depth "${moduledir}" || return 1 + GroupDepths[${group}]=${depth} g_env_must_be_saved='yes' } @@ -613,13 +583,11 @@ subcommand_load() { "${CMD}" "${subcommand}" \ "illegal group name" \ "${group}" - MODULEPATH="" modulepath=() - group+="${PMODULES_MODULEFILES_DIR}" - for overlay in "${OverlayList[@]}"; do - local mod_root="${OverlayDict[${overlay}:mod_root]}" - MODULEPATH="${mod_root}/${group}/:${MODULEPATH}" - modulepath=( "${mod_root}/${group}/" "${modulepath[@]}" ) + group+="/${PMODULES_MODULEFILES_DIR}" + for overlay in "${UsedOverlays[@]}"; do + local mod_root="${OverlayInfo[${overlay}:mod_root]}" + modulepath=( "${mod_root}/${group}" "${modulepath[@]}" ) done fi if [[ -n ${rel_stage} ]]; then @@ -720,9 +688,11 @@ subcommand_load() { LOADEDMODULES="${_LMFILES_}" local dir while read 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}}" - map_moduledir2ol_name overlay "${dir}" done <<< "${MODULEPATH//:/$'\n'}" g_env_must_be_saved='yes' export_env 'LOADEDMODULES' @@ -909,6 +879,12 @@ subcommand_show() { # Find all modules in a given modulepath matching a specific string. # The search can be restricted to certain release stages. # +# Args: +# $1 upvar for results +# $2 search pattern +# $3 release stages +# $4... module directories +# # return list like # modulename_1 rel_stage_1 modulefile_1 ... # @@ -931,8 +907,8 @@ get_available_modules() { # - after loading the parent of a hierarchical group # - if we do a search # - if we create a new hierarchical group - local ol - local group + local ol='' + local group='' find_overlay ol group "${dir}" # if no modules are installed in ${dir}, '*' expands to @@ -941,6 +917,7 @@ get_available_modules() { # implementations. local entries=$(echo *) [[ -n ${entries} ]] || continue + local mod='' # module_name/module_version while read mod; do local add='no' if [[ -n "${ol}" ]]; then @@ -953,7 +930,7 @@ get_available_modules() { local name="${mod%/*}" local key="${dir##/${PMODULES_MODULEFILES_DIR}}/${name}" if [[ -z "${modulenames[${key}]}" ]]; then - if [[ "${OverlayDict[${ol}:type]}" == "${ol_hiding}" ]]; then + if [[ "${OverlayInfo[${ol}:type]}" == "${ol_hiding}" ]]; then modulenames[${key}]="${ol}" else modulenames[${key}]='0' @@ -976,7 +953,7 @@ get_available_modules() { "${mod}" [[ :${used_rel_stages}: =~ :${rel_stage}: ]] || continue - mods+=( "${mod}" ${rel_stage} "${dir}/${mod}" ) + mods+=( "${mod}" ${rel_stage} "${dir}/${mod}" "${ol}" ) dict[${mod}]=1 done < <(${find} -L ${entries} \ \( -type f -o -type l \) \ @@ -1044,12 +1021,18 @@ find_module() { # no version has been specified. This makes it more # difficult. We have to load the newest version taking # the used release stages and flags into account. + + # get list of reverse sorted version numbers (( col += ${#module} + 1 )) modules=( $(${find} -L "${dir}" -type f -not -name ".*" \ -ipath "${dir}/${module}/*" \ | cut -b${col}- \ | sort -rV ) ) + # prepend module name modules=( "${modules[@]/#/${module}/}" ) + + # now modules contains a reverse sorted list of + # available modules in the form name/version for mod in "${modules[@]}"; do # # loop over all used flags. If a module with @@ -1063,7 +1046,7 @@ find_module() { "${dir}" \ "${mod}" std::upvar $2 "${rel_stage}" - [[ :${release}: =~ :${UsedReleaseStages}: ]] && \ + [[ :${rel_stage}: =~ :${UsedReleaseStages}: ]] && \ return 0 done done @@ -1134,7 +1117,7 @@ subcommand_avail() { terse_output() { output_header "$1" local -i i=0 - for (( i=0; i<${#mods[@]}; i+=3 )); do + for (( i=0; i<${#mods[@]}; i+=4 )); do local mod=${mods[i]} local rel_stage=${mods[i+1]} case ${rel_stage} in @@ -1152,7 +1135,7 @@ subcommand_avail() { #...................................................................... machine_output() { - for (( i=0; i<${#mods[@]}; i+=3 )); do + for (( i=0; i<${#mods[@]}; i+=4 )); do printf "%-20s\t%s\n" "${mods[i]}" "${mods[i+1]}" 1>&2 done } @@ -1161,7 +1144,7 @@ subcommand_avail() { # :FIXME: for the time being, this is the same as terse_output! long_output() { output_header "$1" - for (( i=0; i<${#mods[@]}; i+=3 )); do + for (( i=0; i<${#mods[@]}; i+=4 )); do local mod=${mods[i]} local rel_stage=${mods[i+1]} case ${rel_stage} in @@ -1184,7 +1167,7 @@ subcommand_avail() { local -a available_modules=() local mod='' local -i max_length=1 - for ((i=0; i<${#mods[@]}; i+=3)); do + for ((i=0; i<${#mods[@]}; i+=4)); do if [[ ${verbosity_lvl} == 'verbose' ]]; then local rel_stage=${mods[i+1]} case ${rel_stage} in @@ -1355,10 +1338,10 @@ subcommand_use() { print_ol_info(){ local only_used="$1" local ol='' - for ol in "${OverlayList[@]}"; do - [[ ${OverlayDict[${ol}:used]} == ${only_used} ]] || continue - local inst_root="${OverlayDict[${ol}:inst_root]}" - local mod_root="${OverlayDict[${ol}:mod_root]}" + 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]}" local txt="\t${ol}" if [[ ${inst_root} == ${mod_root} ]]; then txt+="\n\t\t${inst_root}" @@ -1366,7 +1349,7 @@ subcommand_use() { txt+="\n\t\t${inst_root} (install root)" txt+="\n\t\t${mod_root} (modulefiles root)" fi - case "${OverlayDict[${ol}:type]}" in + case "${OverlayInfo[${ol}:type]}" in "${ol_hiding}" ) txt+='\n\t\t(hiding modules with same name)' ;; @@ -1445,7 +1428,7 @@ subcommand_use() { "modules are already loaded!" fi - if [[ ${OverlayDict[${ol_name}:used]} == 'yes' ]]; then + if [[ ${OverlayInfo[${ol_name}:used]} == 'yes' ]]; then std::die 3 "%s %s: %s -- %s" \ "${CMD}" "${subcommand}" \ "overlay already in use" \ @@ -1453,39 +1436,38 @@ subcommand_use() { return 0 fi - if [[ "${OverlayDict[${ol_name}:type]}" == "${ol_replacing}" ]]; then + if [[ "${OverlayInfo[${ol_name}:type]}" == "${ol_replacing}" ]]; then # if this overlay replaces groups, we have # to remove the modules made available by # other overlays in these groups for group in ${UsedGroups//:/ }; do # is this group in the to be added overlay? - local dir="${OverlayDict[${ol_name}:mod_root]}/" + local dir="${OverlayInfo[${ol_name}:mod_root]}/" dir+="${group}/${PMODULES_MODULEFILES_DIR}" [[ -d "${dir}" ]] || continue # no dir="/${group}/${PMODULES_MODULEFILES_DIR}" local -a dirs=() - for ol in "${OverlayList[@]}"; do - dirs+=( ${OverlayDict[${ol}:mod_root]}${dir} ) + for ol in "${UsedOverlays[@]}"; do + dirs+=( ${OverlayInfo[${ol}:mod_root]}${dir} ) done std::remove_path MODULEPATH "${dirs[@]}" done fi - + scan_groups "${ol_name}" for group in ${UsedGroups//:/ }; do - local dir="${OverlayDict[${ol_name}:mod_root]}/" + local dir="${OverlayInfo[${ol_name}:mod_root]}/" dir+="${group}/${PMODULES_MODULEFILES_DIR}" if [[ -d "${dir}" ]]; then std::prepend_path MODULEPATH "${dir}" - Dir2OverlayMap[${dir}]="${ol_name}" fi done - OverlayList=( "${ol_name}" "${OverlayList[@]}" ) - OverlayDict[${ol_name}:used]='yes' - export_env OverlayList + UsedOverlays=( "${ol_name}" "${UsedOverlays[@]}" ) + OverlayInfo[${ol_name}:used]='yes' + export_env UsedOverlays g_env_must_be_saved='yes' - scan_groups "${OverlayList[@]}" + scan_groups "${UsedOverlays[@]}" } #.............................................................. @@ -1499,13 +1481,12 @@ subcommand_use() { fi std::append_path UsedGroups "$1" local ol_name - for ol_name in "${OverlayList[@]}"; do - local dir="${OverlayDict[${ol_name}:mod_root]}/$1/${PMODULES_MODULEFILES_DIR}" + for ol_name in "${UsedOverlays[@]}"; do + local dir="${OverlayInfo[${ol_name}:mod_root]}/$1/${PMODULES_MODULEFILES_DIR}" [[ -d "${dir}" ]] || continue std::prepend_path MODULEPATH "${dir}" - Dir2OverlayMap[${dir}]=${ol_name} - [[ "${OverlayDict[${ol_name}:type]}" == "${ol_replacing}" ]] && break + [[ "${OverlayInfo[${ol_name}:type]}" == "${ol_replacing}" ]] && break done } @@ -1522,7 +1503,7 @@ subcommand_use() { UsedFlags+=( "${arg/flag=}" ) return fi - if [[ -v OverlayDict[${arg}:type] ]]; then + if [[ -v OverlayInfo[${arg}:type] ]]; then use_overlay "${arg}" return 0 fi @@ -1533,7 +1514,7 @@ subcommand_use() { if [[ ! -v GroupDepths[${arg}] ]]; then # this scan is required if a new group has been # create inside an used overlay - scan_groups "${!OverlayDict[@]}" + scan_groups "${UsedOverlays[@]}" g_env_must_be_saved='yes' fi if [[ -n ${GroupDepths[${arg}]} ]]; then @@ -1615,46 +1596,46 @@ subcommand_unuse() { "some modules are still loaded!" fi - [[ "${OverlayDict[ol_name]:mod_root}" == "${PMODULES_HOME%%/Tools*}" ]] && \ + [[ "${OverlayInfo[ol_name]:mod_root}" == "${PMODULES_HOME%%/Tools*}" ]] && \ std::die 3 "%s %s: %s -- %s" \ "${CMD}" "${subcommand}" \ "cannot remove base overlay" \ "${ol_name}" - [[ ${OverlayDict[${ol_name}:used]} != 'yes' ]] && \ + [[ ${OverlayInfo[${ol_name}:used]} != 'yes' ]] && \ std::die 3 "%s %s: %s -- %s" \ "${CMD}" "${subcommand}" \ "not an used overlay" \ "${ol_name}" # make sure first index is '0' (it should, but you never know) - OverlayList=( "${OverlayList[@]}" ) - [[ "${ol_name}" != "${OverlayList[0]}" ]] && \ + UsedOverlays=( "${UsedOverlays[@]}" ) + [[ "${ol_name}" != "${UsedOverlays[0]}" ]] && \ std::die 3 "%s %s: %s %s -- %s" \ "${CMD}" "${subcommand}" \ "overlay cannot be removed since" \ "it not on top of the stack" \ "${ol_name}" - if [[ "${OverlayDict[${ol_name}:type]}" == "${ol_replacing}" ]]; then + 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=${OverlayDict[${ol_name}:mod_root]} + local mod_root=${OverlayInfo[${ol_name}:mod_root]} for group in ${UsedGroups//:/ }; do # is this group in the to be removed overlay? local dir="${mod_root}/${group}/${PMODULES_MODULEFILES_DIR}" [[ -d "${dir}" ]] || continue # no dir="/${group}/${PMODULES_MODULEFILES_DIR}" - std::prepend_path MODULEPATH "${OverlayList[@]/%/${dir}}" + std::prepend_path MODULEPATH "${UsedOverlays[@]/%/${dir}}" done fi - OverlayDict[${ol_name}:used]='no' - OverlayList=( "${OverlayList[@]:1}") + OverlayInfo[${ol_name}:used]='no' + UsedOverlays=( "${UsedOverlays[@]:1}") g_env_must_be_saved='yes' - export_env OverlayList + export_env UsedOverlays local dir for dir in "${modulepath[@]}"; do - [[ "${dir}" =~ "${OverlayDict[${ol_name}:mod_root]}" ]] && \ + [[ "${dir}" =~ "${OverlayInfo[${ol_name}:mod_root]}" ]] && \ std::remove_path MODULEPATH "${dir}" done } @@ -1678,7 +1659,7 @@ subcommand_unuse() { fi std::remove_path UsedGroups "${arg}" local overlay - for overlay in "${OverlayList[@]}"; do + for overlay in "${UsedOverlays[@]}"; do local dir="${overlay}/${arg}/${PMODULES_MODULEFILES_DIR}" std::remove_path MODULEPATH "${dir}" done @@ -1701,7 +1682,7 @@ subcommand_unuse() { done return fi - if [[ -v OverlayDict[${arg}:type] ]]; then + if [[ -v OverlayInfo[${arg}:type] ]]; then unuse_overlay "${arg}" return 0 fi @@ -1796,12 +1777,11 @@ init_modulepath() { declare -gx MODULEPATH='' local group local ol - for ol in "${OverlayList[@]}"; do + for ol in "${UsedOverlays[@]}"; do for group in ${UsedGroups//:/ }; do - local dir="${OverlayDict[${ol}:mod_root]}/${group}/${PMODULES_MODULEFILES_DIR}" + local dir="${OverlayInfo[${ol}:mod_root]}/${group}/${PMODULES_MODULEFILES_DIR}" if [[ -d "${dir}" ]]; then std::prepend_path MODULEPATH "${dir}" - Dir2OverlayMap[${dir}]="${ol}" fi done done @@ -1842,6 +1822,10 @@ pmodules_init() { done g_env_must_be_saved='yes' } + init_overlay_vars() { + declare -ag UsedOverlays=( 'base' ) + OverlayInfo['base:used']='yes' + } pm::read_config @@ -1856,6 +1840,7 @@ pmodules_init() { init_overlay_vars init_modulepath init_manpath + save_env export_env \ LOADEDMODULES \ _LMFILES_ \ @@ -1910,13 +1895,15 @@ subcommand_purge() { # is a Pmodule module loaded? # if yes, save name in variable 'pmodule' - local pmodule='' + # We also have to save PMODULES_HOME since it will be + # unset while sourcing the shell's init script. IFS=':' local -a lmfiles=($_LMFILES_) unset IFS for f in "${lmfiles[@]}"; do if [[ $f == */${PMODULES_MODULEFILES_DIR}/Pmodules/* ]]; then - pmodule="${f##*/${PMODULES_MODULEFILES_DIR}/}" + local pm_home="${PMODULES_HOME}" + local pmodule="${f##*/${PMODULES_MODULEFILES_DIR}/}" break; fi done @@ -1946,16 +1933,18 @@ subcommand_purge() { echo "${error}" 1>&2 fi - if [[ -n "${pmodule}" ]]; then + if [[ -v pmodule ]]; then # reload a previously loaded Pmodule module # stderr is redirected to /dev/null, otherwise # we may get output like # 'unstable module has been loaded' + PMODULES_HOME="${pm_home}" + export_env PMODULES_HOME subcommand_load "${pmodule}" 2> /dev/null fi init_modulepath - export_env MODULEPATH PMODULES_HOME + export_env MODULEPATH } ############################################################################## @@ -2045,7 +2034,6 @@ subcommand_clear() { "no arguments allowed" fi pmodules_init - export_env LOADEDMODULES MODULEPATH _LMFILES_ } ############################################################################## @@ -2130,7 +2118,7 @@ subcommand_search() { local fmt='' print_default() { - fmt="%-${max_len_modulename}s %-10s %-12s %-s" + fmt="%-${max_len_modulename}s %-10s %-12s %-12s %-s" if [[ ${opt_print_header} == 'yes' ]]; then func_print_header='print_header_default' else @@ -2141,7 +2129,7 @@ subcommand_search() { print_header_default() { std::info '' - std::info "${fmt}" "Module" "Rel.stage" "Group" "Requires" + std::info "${fmt}" "Module" "Rel.stage" "Group" "Overlay" "Requires" std::info '-%.0s' $(seq 1 ${cols}) } @@ -2154,12 +2142,12 @@ subcommand_search() { std::info "${str}" } if [[ "${opt_wrap}" == 'no' ]]; then - local deps="${@:5}" - local str=$(printf "${fmt}" "$1" "$2" "$3" "${deps[@]}") + local deps="${@:6}" + local str=$(printf "${fmt}" "$1" "$2" "$3" "$5" "${deps[@]}") write_line "${str}" else - local deps=( "${@:5}" ) - local str=$(printf "${fmt}" "$1" "$2" "$3" "${deps[0]}") + local deps=( "${@:6}" ) + local 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]}" @@ -2173,21 +2161,26 @@ subcommand_search() { } print_verbose() { - fmt="%-${max_len_modulename}s %-10s %-12s %-s" + fmt="%-${max_len_modulename}s %-12s %-14s %-s" func_print_header='print_header_verbose' func_print_line='print_line_verbose' } print_header_verbose() { std::info '' - std::info "${fmt}" "Module" "Rel.stage" "Group" "Dependencies/Modulefile" - std::info '-%.0s' $(seq 1 ${cols}) + #std::info "${fmt}" "Module" "Rel.stage" "Group" "Overlay" "Dependencies/Modulefile" + #std::info '-%.0s' $(seq 1 ${cols}) } print_line_verbose() { - local deps="${@:5}" - std::info "${fmt}" "$1" "$2" "$3" "dependencies: ${deps}" - std::info "${fmt}" "" "" "" "modulefile: $4" + local deps="${@:6}" + [[ -z ${deps} ]] && deps="(none)" + std::info "$1:" + std::info " release stage: $2" + std::info " group: $3" + std::info " overlay: $5" + std::info " modulefile: $4" + std::info " dependencies: ${deps}" } # print full modulefile names only @@ -2228,7 +2221,7 @@ subcommand_search() { ${func_print_header} while read -a toks; do ${func_print_line} "${toks[@]}" - done < <("${sort}" --version-sort -k 1,1 -k 4,4 -k 5,5 "${tmpfile}" | \ + done < <("${sort}" --version-sort -k 1,1 -k 6,6 -k 7,7 "${tmpfile}" | \ ${awk} "${with_modules}") } @@ -2271,10 +2264,11 @@ subcommand_search() { "${opt_use_rel_stages}" \ "${modulepath[@]}" \ - for (( i=0; i<${#mods[@]}; i+=3 )); do + for (( i=0; i<${#mods[@]}; i+=4 )); do local name=${mods[i]} local rel_stage=${mods[i+1]} local modulefile=${mods[i+2]} + local ol=${mods[i+3]} if (( ${#name} > max_len_modulename)); then max_len_modulename=${#name} @@ -2302,6 +2296,7 @@ subcommand_search() { fi echo ${name} ${rel_stage} ${group} ${modulefile} \ + ${ol} \ ${deps[@]} >> "${tmpfile}" done done @@ -2405,7 +2400,10 @@ subcommand_search() { shift done if [[ -z "${src_prefix}" ]]; then - local -a src_prefix=( "${OverlayList[@]}" ) + local ol='' + for ol in "${UsedOverlays[@]}"; do + src_prefix+=( "${OverlayInfo[${ol}:mod_root]}" ) + done fi if [[ "${opt_use_rel_stages}" == ":" ]]; then @@ -2740,21 +2738,6 @@ if [[ -z "${Subcommands[${subcommand}]}" ]]; then std::die 1 "${CMD}: unknown sub-command -- ${subcommand}" fi -init_overlay_vars() { - declare -ag OverlayList=( 'base' ) - OverlayDict['base:used']='yes' - #declare -Ag Dir2OverlayMap=() - for ol in "${OverlayList[@]}"; do - local group - for group in ${UsedGroups//:/ }; do - local dir="${OverlayDict[${ol}:mod_root]}/${group}/${PMODULES_MODULEFILES_DIR}" - if [[ -d "${dir}" ]]; then - Dir2OverlayMap[${dir}]=${ol} - fi - done - done -} - case ${subcommand} in add ) subcommand='load' @@ -2777,51 +2760,25 @@ if [[ -n ${PMODULES_ENV} ]]; then eval "$("${base64}" -d <<< "${PMODULES_ENV}" 2>/dev/null)" fi if [[ -z ${Version} ]] || [[ ${Version} != ${PMODULES_VERSION} ]]; then + # this can only happen if the last command was + # module load Pmodules/${PMODULES_VERSION} + # + # the values these two variables must be saved before initialising + declare _tmp_loaded_modules_="${LOADEDMODULES}" + declare _tmp_lmfiles_="${_LMFILES_}" + pmodules_init - g_env_must_be_saved='yes' + + # restore and export + LOADEDMODULES="${_tmp_loaded_modules_}" + _LMFILES_="${_tmp_lmfiles_}" + export_env \ + LOADEDMODULES \ + _LMFILES_ fi -#if [[ -n ${PMODULES_ENV} ]]; then -# eval "$("${base64}" -d <<< "${PMODULES_ENV}" 2>/dev/null)" -# if [[ -z ${Version} ]] || [[ ${Version} != ${PMODULES_VERSION} ]]; then -# # the Pmodules version changed! -# declare -g Version="${PMODULES_VERSION}" -# pm::read_config -# init_overlay_vars -# # renamed in version 1.0.0rc10 and type changed from -# # associative array to normal array -# if [[ -v UseFlags ]]; then -# declare -a UsedFlags=( "${!UseFlags[@]}" ) -# unset UseFlags -# fi -# if [[ ! -v UsedFlags ]]; then -# declare -a UsedFlags=() -# fi -# if [[ -v UsedReleases ]]; then -# declare -- UsedReleaseStages="${UsedReleases}" -# unset UsedReleases -# fi -# if [[ -v PMODULES_DEFAULT_GROUPS ]]; then -# declare -- DefaultGroups="${PMODULES_DEFAULT_GROUPS}" -# unset PMODULES_DEFAULT_GROUPS -# fi -# if [[ -v PMODULES_DEFINED_RELEASES ]]; then -# declare -- ReleaseStages="${PMODULES_DEFINED_RELEASES}" -# unset PMODULES_DEFINED_RELEASES -# fi -# if [[ -v PMODULES_DEFAULT_RELEASES ]]; then -# declare -- DefaultReleaseStages="${PMODULES_DEFAULT_RELEASES}" -# unset PMODULES_DEFAULT_RELEASES -# fi -# g_env_must_be_saved='yes' -# fi -#else -# pmodules_init -# g_env_must_be_saved='yes' -#fi - if (( ${#GroupDepths[@]} == 0 )); then - scan_groups "${OverlayList[@]}" + scan_groups "${UsedOverlays[@]}" g_env_must_be_saved='yes' fi diff --git a/Pmodules/modulefile b/Pmodules/modulefile index 40eba48..866568f 100644 --- a/Pmodules/modulefile +++ b/Pmodules/modulefile @@ -10,9 +10,8 @@ Pmodules are a hierarchical module environment based on Environment Modules. " # -# It might be that '${PMODULES_HOME}' is in PATH. -# Why? With older version the PATH might have been set without loading -# a module. +# Older versions add '$PREFIX/bin' to PATH without loading Pmodules as +# a module. Remove it ... # if { [module-info mode load] } { set PATH ":$::env(PATH):" @@ -21,4 +20,9 @@ if { [module-info mode load] } { remove-path PATH $str } } -} + remove-path C_INCLUDE_PATH "$PREFIX/include" + remove-path CPLUS_INCLUDE_PATH "$PREFIX/include" + + set shell [module-info shell] + puts "source \"$PREFIX/init/$shell\"" +} \ No newline at end of file diff --git a/Pmodules/zsh b/Pmodules/zsh index 0b5d282..b6b800b 100644 --- a/Pmodules/zsh +++ b/Pmodules/zsh @@ -34,6 +34,14 @@ module() { } export -f module +modbuild(){ + ( + eval $("${PMODULES_HOME}/bin/modulecmd" bash load System:bash) + "${PMODULES_HOME}/bin/modbuild" "$@" + ) +} +export -f modbuild + # Local Variables: # mode: sh # sh-basic-offset: 8 diff --git a/Tools/Tcl/build b/Tools/Tcl/build deleted file mode 100755 index d1ceea6..0000000 --- a/Tools/Tcl/build +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env modbuild - -pbuild::set_download_url "https://prdownloads.sourceforge.net/tcl/tcl$V-src.tar.gz" -pbuild::install_docfiles 'license.terms' 'README.md' - -pbuild::configure() { - case ${OS} in - Linux ) - srcdir="${SRC_DIR}/unix" - ;; - Darwin ) - srcdir="${SRC_DIR}/macosx" - ;; - esac - "${srcdir}"/configure \ - --prefix="${PREFIX}" \ - --bindir="${PREFIX}/${UTILBIN_DIR}" \ - --enable-shared=no \ - || exit 1 -} - -pbuild::post_install() { - { cd "${PREFIX}/${UTILBIN_DIR}" && rm -f tclsh && ln -fs tclsh${V%.*} tclsh; }; -} - diff --git a/Tools/bash/build b/Tools/bash/build deleted file mode 100755 index ff8489f..0000000 --- a/Tools/bash/build +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env modbuild - -pbuild::set_download_url "https://ftp.gnu.org/gnu/$P/$P-$V.tar.gz" -pbuild::add_configure_args "--bindir=${PREFIX}/${UTILBIN_DIR}" - diff --git a/Tools/coreutils/build b/Tools/coreutils/build deleted file mode 100755 index c561e2a..0000000 --- a/Tools/coreutils/build +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env modbuild - -pbuild::set_download_url "http://ftp.gnu.org/gnu/coreutils/$P-$V.tar.xz" -pbuild::add_configure_args "--bindir=${PREFIX}/${UTILBIN_DIR}" diff --git a/Tools/coreutils/build.new b/Tools/coreutils/build.new deleted file mode 100755 index 63030c7..0000000 --- a/Tools/coreutils/build.new +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env pbuild - -pmodules.configure() { - "${MODULE_SRCDIR}"/configure \ - --prefix="${PREFIX}" \ - || exit 1 -} - -pmodules.add_to_group 'Tools' -pmodules.make_all diff --git a/Tools/findutils/build b/Tools/findutils/build deleted file mode 100755 index d63f114..0000000 --- a/Tools/findutils/build +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env modbuild - -pbuild::set_download_url "https://ftp.gnu.org/pub/gnu/$P/$P-$V.tar.xz" -pbuild::add_configure_args "--bindir=${PREFIX}/${UTILBIN_DIR}" diff --git a/Tools/getopt/Makefile.patch b/Tools/getopt/Makefile.patch deleted file mode 100644 index 6aa2403..0000000 --- a/Tools/getopt/Makefile.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff -u getopt-1.1.6.orig//Makefile getopt-1.1.6/Makefile ---- getopt-1.1.6.orig//Makefile 2014-11-24 04:33:39.000000000 -0800 -+++ getopt-1.1.6/Makefile 2016-10-27 08:27:52.000000000 -0700 -@@ -61,7 +61,7 @@ - -$(RM) $(objects) $(binaries) - - getopt: $(objects) -- $(CC) $(LDFLAGS) -o $@ $(objects) -+ $(CC) -o $@ $(objects) $(LDFLAGS) - - install: getopt install_po - $(INSTALL) -m 755 -d $(DESTDIR)$(bindir) $(DESTDIR)$(man1dir) diff --git a/Tools/getopt/build b/Tools/getopt/build deleted file mode 100755 index 81d8433..0000000 --- a/Tools/getopt/build +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env modbuild -set -x - -pbuild::set_download_url "http://frodo.looijaard.name/system/files/software/getopt/getopt-1.1.6.tar.gz" -pbuild::compile_in_sourcetree - -pbuild::pre_prep_Linux() { - pbuild::add_patch "Makefile.patch" -} - -pbuild::configure() { - : -} - -pbuild::compile() { - declare -x C_INCLUDE_PATH="${PREFIX}/include" - declare -x LIBRARY_PATH="${PREFIX}/lib" - case ${OS} in - Linux ) - declare -x C_INCLUDE_PATH="${PREFIX}/include" - declare -x LIBRARY_PATH="${PREFIX}/lib" - declare -x LDFLAGS="-lintl -L${PREFIX}/lib" - ;; - Darwin ) - if [[ ! -d '/opt/local/bin' ]] || [[ ! -x '/opt/local/bin/msgfmt' ]]; then - std::die 1 "gettext port from Macports is required to build 'getopt'!" - fi - PATH+=':/opt/local/bin' - declare -x C_INCLUDE_PATH='/opt/local/include' - declare -x LDFLAGS="/opt/local/lib/libintl.a /opt/local/lib/libiconv.a -framework CoreFoundation" - ;; - esac - - make -e all || exit 1 - declare -x DESTDIR="${PREFIX}" - declare -x prefix='' || exit 1 - PATH="${PREFIX}/${UTILBIN_DIR}:${PATH}" - make -e install - mv "${PREFIX}/bin/getopt" "${PREFIX}/${UTILBIN_DIR}" -} - -pbuild::install() { - : -} - -pbuild::cleanup_build() { - : -} - -# Local Variables: -# mode: sh -# sh-basic-offset: 8 -# tab-width: 8 -# End: diff --git a/Tools/gettext/build b/Tools/gettext/build deleted file mode 100755 index 776185e..0000000 --- a/Tools/gettext/build +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env modbuild - -pbuild::set_download_url "https://ftp.gnu.org/pub/gnu/$P/$P-$V.tar.gz" - -pbuild::add_configure_args "--prefix=${PREFIX}" -pbuild::add_configure_args "--bindir=${PREFIX}/${UTILBIN_DIR}" -pbuild::add_configure_args "--disable-java" -pbuild::add_configure_args "--disable-threads" -pbuild::add_configure_args "--disable-shared" -pbuild::add_configure_args "--enable-relocatable" -pbuild::add_configure_args "--disable-openmp" -pbuild::add_configure_args "--disable-acl" -pbuild::add_configure_args "--disable-curses" -pbuild::add_configure_args "--with-included-gettext" -pbuild::add_configure_args "--without-libiconv-prefix" -pbuild::add_configure_args "--without-libintl-prefix" -pbuild::add_configure_args "--with-included-libxml" -pbuild::add_configure_args "--with-pic=yes" - diff --git a/Tools/modules/build b/Tools/modules/build deleted file mode 100755 index 14399de..0000000 --- a/Tools/modules/build +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env modbuild - -# use system gcc to compile -declare -rx CC=gcc - -pbuild::set_download_url "https://amas.web.psi.ch/Downloads/$P/$P-$V.tar.bz2" - -TCL_DIR="${PMODULES_HOME}" - -PATH="${TCL_DIR}/bin:${PATH}" - -pbuild::configure() { - case ${OS} in - Linux ) - declare -x LIBS="-lz -lpthread" - ;; - Darwin ) - declare -x LIBS="-lz -framework CoreFoundation" - ;; - esac - CPPFLAGS="-DUSE_INTERP_ERRORLINE" "${SRC_DIR}"/configure \ - --prefix="${PREFIX}" \ - --exec-prefix="${PREFIX}" \ - --with-module-path="${PMODULES_HOME%%/Tools*}/Tools/${PMODULES_MODULEFILES_DIR}" \ - --with-tcl="${TCL_DIR}/lib" \ - --without-x \ - --disable-versioning \ - || exit 1 -} - -pbuild::post_install() { - rm -v "${PREFIX}/Modules/bin/add.modules" - rm -v "${PREFIX}/Modules/bin/mkroot" - rm -rfv "${PREFIX}/Modules/modulefiles" - mv -v "${PREFIX}/Modules/share/man/man1/module.1 ${PREFIX}/share/man/man1" - mv -v "${PREFIX}/Modules/share/man/man4/modulefile.4 ${PREFIX}/share/man/man4" - rmdir "${PREFIX}/Modules/bin" - rmdir "${PREFIX}/Modules/share/man/man1" - rmdir "${PREFIX}/Modules/share/man/man4" - rmdir "${PREFIX}/Modules/share/man" - rmdir "${PREFIX}/Modules/share" - rmdir "${PREFIX}/Modules" - rm -f "${PREIX}/init/{ksh,perl.pm,python.py,ruby.rb,cmake,.modulespath}" - cp -v "${BUILD_DIR}/modulecmd" "${PREFIX}/libexec/modulecmd.bin" || exit 1 -} - -# fake module command -module() { - : -} - -# Local Variables: -# mode: sh -# sh-basic-offset: 8 -# tab-width: 8 -# End: diff --git a/Tools/tcllib/build b/Tools/tcllib/build deleted file mode 100755 index ea151ba..0000000 --- a/Tools/tcllib/build +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env modbuild - -pbuild::set_download_url "https://core.tcl-lang.org/tcllib/uv/$P-$V.tar.xz" - - diff --git a/build b/build index 232a760..99f8959 100755 --- a/build +++ b/build @@ -1,56 +1,84 @@ #!/usr/bin/env bash # -if [ "${BASH_VERSINFO[0]}" -lt 4 ]; then - echo "BASH version >= 4 is required and must be available in PATH!" 1>&2 + +declare -rx VERSION='1.1.10' + +if (( "${BASH_VERSINFO[0]}" < 5 )); then + echo "BASH version 5.0 or newer is required and must be available in PATH!" 1>&2 exit 1 fi -declare BOOTSTRAP_DIR=$(dirname "$0") -source "${BOOTSTRAP_DIR}/Pmodules/libstd.bash" || { echo "Oops!" 1>&2; exit 42; } -source "${BOOTSTRAP_DIR}/Pmodules/libpbuild.bash" || { echo "Oops!" 1>&2; exit 42; } +set -o nounset +set -o pipefail +shopt -s nullglob -declare -r BOOTSTRAP_DIR=$(std::get_abspath "${BOOTSTRAP_DIR}") +declare -r BOOTSTRAP_DIR="$(cd "$(dirname "$0")" && pwd -P)" declare -r SRC_DIR="${BOOTSTRAP_DIR}/Pmodules" -declare -x PMODULES_ROOT -declare -x PMODULES_HOME -declare -x PMODULES_DISTFILESDIR -declare -x PMODULES_TMPDIR +source "${SRC_DIR}/libstd.bash" || { echo "Oops!" 1>&2; exit 42; } +source "${SRC_DIR}/libpbuild.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' -declare -rx CONFIG_FILE='Pmodules.yaml' -declare -rx VERSIONS_CFG_FILE="${CONFIG_DIR}/versions.conf" - -# defaults -declare -rx DEFAULT_PMODULES_ROOT='/opt/psi' -declare -rx DEFAULT_DISTFILES_DIR="var/distfiles" -declare -rx DEFAULT_TMPDIR='var/tmp/${USER}' +declare -rx CONFIG_FILE="${CONFIG_DIR}/Pmodules.yaml" # directory where the required tools will be installed (like bash, tclsh, etc) declare -rx UTILBIN_DIR='libexec' +# defaults +declare -rx DEFAULT_INSTALL_ROOT='/opt/psi' +declare -rx DEFAULT_DISTFILES_DIR='var/distfiles' +declare -rx DEFAULT_TMP_DIR='var/tmp/${USER}' + #----------------------------------------------------------------------------- # -# Get version from config file. -# -# The format of the config file is: -# -# -# -get_version() { - local -r name="$1" - echo $(awk "/^$1[[:blank:]]/ {print \$2}" "${VERSIONS_CFG_FILE}") +read_config_file() { + local fname="$1" + if [[ ! -r "${fname}" ]]; then + std::die 1 "Configuration file '${fname}' does not exist or is not readable!" + fi + + eval $(std::parse_yaml "${fname}" '') || \ + std::die 1 "Cannot read configuration file '${fname}'" + + declare -xg INSTALL_ROOT="${Overlays_base_install_root}" + if [[ -z "${INSTALL_ROOT}" ]]; then + std::die 1 "Error in configuration file '${fname}': install root not defined!" + fi + declare -xg PREFIX="${INSTALL_ROOT}/${PMOD_DIR}" + declare -xg DOWNLOADS_DIR="${DistfilesDir:-${INSTALL_ROOT}/${DEFAULT_DISTFILES_DIR}}" + declare -xg TMP_DIR="${TmpDir:-${INSTALL_ROOT}/${DEFAULT_TMP_DIR}}" } +#----------------------------------------------------------------------------- +# The next functions are used in the sub-commands, if an illegal option +# or argument has been passed. # -# the Pmodules version is defined in the config file -# -declare -rx PMODULES_VERSION=$(get_version 'Pmodules') +illegal_option(){ + local subcmd="$1" + local opt="$2" + std::die 1 \ + "%s: %s -- %s" \ + "$(basename $0) ${subcmd}" \ + "Illegal option" \ + "${opt}" +} + +illegal_arg(){ + local subcmd="$1" + local arg="$2" + std::die 1 \ + "%s: %s -- %s" \ + "$(basename $0) ${subcmd}" \ + "Illegal argument" \ + "${arg}" +} #----------------------------------------------------------------------------- -# -usage() { - local prog=$(basename $0) +# help for sub-command 'help' (usage) +build::help_help(){ + local prog="$(basename "$0")" echo " Usage: ${prog} help|configure|compile|install @@ -73,26 +101,36 @@ to get help for a specific sub-command. } #----------------------------------------------------------------------------- +# sub-command 'help' # -pmodules::help() { - if (( $# > 1 )); then - usage +# print help for sub-commands +# +build::help() { + if (( $# == 0 )); then + build::help_help + else + case $1 in + configure|compile|install ) + build::help_$1 + ;; + help ) + build::help_help + ;; + -* ) + illegal_option 'help' "$1" + ;; + * ) + std::error "No such command -- $1" + build::help_help + ;; + esac fi - case $1 in - configure|compile|install ) - pmodules::help_$1 - ;; - * ) - echo -en "$1 - invalid sub-command!\n" 1>&2 - usage - ;; - esac } - #----------------------------------------------------------------------------- +# help for sub-command 'configure' # -pmodules::help_configure() { +build::help_configure() { echo " Usage: $(basename $0) configure [OPTION...] @@ -100,21 +138,24 @@ Configure and setup a new Pmodules environment. You need permissions to write to the installation root. Options: ---prefix=DIR +--install_root=DIR Root of the Pmodules environment installation. Everything will be installed in a directory hierarchy with 'DIR' as prefix. - The default is '${DEFAULT_PMODULES_ROOT}'. + The default is '${DEFAULT_INSTALL_ROOT}'. --distfilesdir=DIR Directory where downloaded files are stored. - The default is 'PREFIX/${DEFAULT_DISTFILES_DIR}' in the + The default is '${DEFAULT_INSTALL_ROOT}/${DEFAULT_DISTFILES_DIR}' in the Pmodules root directory. --tmpdir=DIR Directory for temporary files. - The default is 'PREFIX/${DEFAULT_TMPDIR}' + The default is '${DEFAULT_INSTALL_ROOT}/${DEFAULT_TMP_DIR}' ---help +--force|-f + Override existing configuration. + +--help|-h|-? Print this help text. " 1>&2 @@ -122,110 +163,123 @@ Options: } #----------------------------------------------------------------------------- +# sub-command 'configure' # -pmodules::configure() { - local prefix="${PMODULES_ROOT:-${DEFAULT_PMODULES_ROOT}}" - local distfilesdir='' - local tmpdir='' - local config_file='' +# Create basic directory hierachy and the configuration file in the given +# installation root directory +# +build::configure() { local opt_force='no' while (( $# > 0 )); do case "$1" in - --prefix ) - prefix="$2" - shift 1 - ;; - --prefix=* ) - prefix="${1#*=}" + --install_root | --install_root=* ) + if [[ $1 == *=* ]]; then + INSTALL_ROOT="${1#*=}" + else + INSTALL_ROOT="$2" + shift 1 + fi ;; --distfilesdir ) - distfilesdir="$2" - shift 1 - ;; - --distfilesdir=* ) - distfilesdir="${1#*=}" + if [[ $1 == *=* ]]; then + DOWNLOADS_DIR="${1#*=}" + else + DOWNLOADS_DIR="$2" + shift 1 + fi ;; -f | --force ) opt_force='yes' ;; - --tmpdir ) - tmpdir="$2" - shift 1 - ;; - --tmpdir=* ) - tmpdir="${1#*=}" + --tmpdir | --tmpdir=* ) + if [[ $1 == *=* ]]; then + TMP_DIR="${1#*=}" + else + TMP_DIR="$2" + shift 1 + fi ;; --help | -h | -\? ) - pmodules::help_configure + build::help_configure ;; -* ) - std::die 1 "$1: illegal option" + illegal_option 'configure' "$1" ;; * ) - std::die 1 "$1: illegal argument to sub-command 'configure'." + illegal_arg 'configure' "$1" ;; esac shift 1 done - if [[ ! -d ${prefix} ]]; then - echo "The root directory '${prefix}' does not exist, trying to create it..." - if ! mkdir -p "${prefix}"; then - std::die 1 "Creating the root directory failed!\nAborting..." + : ${INSTALL_ROOT:=${DEFAULT_INSTALL_ROOT}} + : ${DOWNLOADS_DIR:=${INSTALL_ROOT}/${DEFAULT_DISTFILES_DIR}} + : ${TMP_DIR:=${INSTALL_ROOT}/${DEFAULT_TMP_DIR}} + PREFIX="${INSTALL_ROOT}/${PMOD_DIR}" + + #--- + # check/create the install root + if [[ ! -d ${INSTALL_ROOT} ]]; then + std::info "%s\n%s" \ + "The root directory '${INSTALL_ROOT}' does not exist!" \ + "Trying to create it..." + if ! mkdir -p "${INSTALL_ROOT}"; then + std::die 1 "%s\n%s" \ + "Creating the root directory failed!" \ + "Aborting..." fi fi - if [[ ! -w ${prefix} ]]; then - std::die 1 "The root directory '${prefix}' is not writable!\nAborting..." + if [[ ! -w ${INSTALL_ROOT} ]]; then + std::die 1 "%s\n%s" \ + "The root directory '${INSTALL_ROOT}' is not writable!" \ + "Aborting..." fi - mkdir -p "${prefix}/${CONFIG_DIR}" || \ + + #--- + # check/create YAML config file in install root + mkdir -p "${INSTALL_ROOT}/${CONFIG_DIR}" || \ std::die 1 "Aborting..." - local config_file="${prefix}/${CONFIG_DIR}/${CONFIG_FILE}" - if [[ "${opt_force}" == 'yes' ]]; then - rm -f "${config_file}" + local config_file="${INSTALL_ROOT}/${CONFIG_FILE}" + if [[ "${opt_force}" != 'yes' ]] && [[ -e "${config_file}" ]]; then + std::die 1 "%s\n%s" \ + "The Pmodules environment in '${INSTALL_ROOT}' has already been configured!" \ + "Use the option --force to override. Aborting..." fi - if [[ -e "${config_file}" ]]; then - std::die 1 "The Pmodules environment has already been configured! -Use the option --force to override.\nAborting..." - fi - - [[ -z "${distfilesdir}" ]] && distfilesdir="${prefix}/${DEFAULT_DISTFILES_DIR}" - [[ -z "${tmpdir}" ]] && tmpdir="${prefix}/${DEFAULT_TMPDIR}" - - sed_cmd="s:@INSTALL_ROOT@:${prefix}:g;" - sed_cmd+="s:@PMODULES_DISTFILESDIR@:${distfilesdir}:g;" - sed_cmd+="s:@PMODULES_TMPDIR@:${tmpdir}:g;" - sed_cmd+="s:@PMODULES_VERSION@:${PMODULES_VERSION}:g" + sed_cmd="s:@INSTALL_ROOT@:${INSTALL_ROOT}:g;" + sed_cmd+="s:@PMODULES_DISTFILESDIR@:${DOWNLOADS_DIR}:g;" + sed_cmd+="s:@PMODULES_TMPDIR@:${TMP_DIR}:g;" + sed_cmd+="s:@PMODULES_VERSION@:${VERSION}:g" - sed "${sed_cmd}" "${BOOTSTRAP_DIR}/${CONFIG_DIR}/${CONFIG_FILE}.in" \ + sed "${sed_cmd}" "${BOOTSTRAP_DIR}/${CONFIG_FILE}.in" \ > "${config_file}" || \ std::die 1 "Cannot create configuration file in Pmodules root\nAborting..." - sed "${sed_cmd}" "${SRC_DIR}/libpmodules.bash.in" > "${SRC_DIR}/libpmodules.bash" - chmod 0755 "${SRC_DIR}/libpmodules.bash" - - read_config_file "${config_file}" - install -d -m 0755 "${PMODULES_HOME}/bin" - install -d -m 0755 "${PMODULES_HOME}/init" - install -d -m 0755 "${PMODULES_HOME}/lib" - install -d -m 0755 "${PMODULES_HOME}/libexec" - install -d -m 0755 "${PMODULES_ROOT}/Tools/modulefiles/Pmodules" - install -d -m 0755 "${PMODULES_ROOT}/Libraries/modulefiles" - install -d -m 0755 "${PMODULES_ROOT}/Programming/modulefiles" - install -d -m 0755 "${distfilesdir}" + #--- + # create basic directories + install -d -m 0755 \ + "${INSTALL_ROOT}/Tools/modulefiles/Pmodules" \ + "${INSTALL_ROOT}/Libraries/modulefiles" \ + "${INSTALL_ROOT}/Programming/modulefiles" \ + "${DOWNLOADS_DIR}" || \ + std::die 1 "%s" \ + "Creating basic directories failed\n" \ + "Aborting..." + #--- echo "Configuration:" - echo " root of Pmodules environment: ${prefix}" - echo " Pmodule prefix: ${PMODULES_HOME}" - echo " tmp directory: ${tmpdir}" - echo " store for downloaded files: ${distfilesdir}" + echo " root of Pmodules environment: ${INSTALL_ROOT}" + echo " Pmodule prefix: ${PREFIX}" + echo " tmp directory: ${TMP_DIR}" + echo " store for downloaded files: ${DOWNLOADS_DIR}" echo "Done..." } #----------------------------------------------------------------------------- +# help for sub-command 'compile' # -pmodules::help_compile() { +build::help_compile() { echo " Usage: $(basename $0) compile [OPTION...] @@ -233,22 +287,14 @@ Compile and install the required tools for a new Pmodules environment. ou need the permissions to write to the installation root. Options: ---prefix=DIR - Root of the Pmodules environment installation. The root of the - installation must be either specified via this option or the - environment variable PMODULES_ROOT. If this option is used and - the PMODULES_ROOT is set, the directory specified with this - option will be used. +--install_root=DIR + Root of the Pmodules environment installation. Everything will be + installed in a directory hierarchy with 'DIR' as prefix. + The default is '${DEFAULT_INSTALL_ROOT}'. ---debug - Enable verbose/debug output. - ---disable-cleanup ) +--disable-cleanup Do not cleanup the tmp directory after compilation and installation. ---force | -f - Force compilation. - --help Print this help text. @@ -256,270 +302,208 @@ Options: std::die 1 "" } -pmodules::compile() { - build () { - local -r name="$1" - local -r version=$(get_version "${name}") - shift - - "${BOOTSTRAP_DIR}/Pmodules/modbuild.in" \ - "--config=${config_file}" \ - "--disable-cleanup" \ - "--force-rebuild" \ - "--debug" \ - "--verbose" \ - "${BOOTSTRAP_DIR}/Tools/${name}/build" \ - "${build_opts[@]}" "$@" "${version}" || \ - std::die 3 "Compiling '${name}' failed!" - # :FIXME: this must be fixed in modbuild! - rm -f "${BOOTSTRAP_DIR}/Tools/modulefiles/.release-$V" - } - - local prefix="${PMODULES_ROOT:-${DEFAULT_PMODULES_ROOT}}" +#----------------------------------------------------------------------------- +# sub-command 'compile' +# +# compile all required tools like bash, tclsh etc for a Pmodules module. +# The version is defined at the beginning of this file. +# +build::compile() { local opt_force='no' - local config_file='' + local opt_cleanup='yes' while (( $# > 0 )); do case $1 in - --prefix ) - prefix="$2" - shift 1 - ;; - --prefix=* ) - prefix="${1#*=}" + --install_root | --install_root=* ) + if [[ $1 == *=* ]]; then + INSTALL_ROOT="${1#*=}" + else + INSTALL_ROOT="$2" + shift 1 + fi ;; --disable-cleanup ) - build_opts+=( "$1" ) - ;; - --debug ) - build_opts+=( "$1" ) - ;; - -f | --force ) - opt_force='yes' + opt_cleanup='no' ;; --help | -h | -\? ) - pmodules::help_compile + build::help_compile ;; -* ) - std::die 1 "$1: illegal option" + illegal_option 'compile' "$1" ;; * ) - std::die 1 "$1: illegal argument for sub-command 'compile'." + illegal_arg 'compile' "$1" ;; esac shift 1 done + : ${INSTALL_ROOT:=${DEFAULT_INSTALL_ROOT}} + PREFIX="${INSTALL_ROOT}/${PMOD_DIR}" - local config_file="${prefix}/${CONFIG_DIR}/${CONFIG_FILE}" - read_config_file "${config_file}" - install -d -m 0755 "${PMODULES_HOME}/bin" - install -d -m 0755 "${PMODULES_HOME}/init" - install -d -m 0755 "${PMODULES_HOME}/lib" - install -d -m 0755 "${PMODULES_HOME}/libexec" + read_config_file "${INSTALL_ROOT}/${CONFIG_FILE}" echo "Configuration:" - echo " root of Pmodules environment: ${prefix}" - echo " Pmodule prefix: ${PMODULES_HOME}" + echo " root of Pmodules environment: ${INSTALL_ROOT}" + echo " Pmodule prefix: ${PREFIX}" - if [[ "${OS}" == 'Darwin' ]]; then - if [[ ! -f "${PMODULES_HOME}/${UTILBIN_DIR}/getopt" ]] || [[ ${opt_force} == 'yes' ]]; then - build getopt - fi + install -m 0755 -d "${PREFIX}"/{bin,init,lib,libexec} \ - if [[ ! -f "${PMODULES_HOME}/${UTILBIN_DIR}/find" ]] || [[ ${opt_force} == 'yes' ]]; then - build findutils - fi + for recipe in recipes/[0-9]*; do + "./${recipe}" "${PREFIX}" + done + if [[ "${opt_cleanup}" == 'yes' ]]; then + rm -rf "${TMP_DIR}/*" + rm -f "${PREFIX}/lib/libtcl*.a" + rm -rf "${PREFIX}/include" fi - - if [[ ! -f "${PMODULES_HOME}/${UTILBIN_DIR}/bash" ]] || [[ ${opt_force} == 'yes' ]]; then - build bash - fi - - if [[ ! -e "${PMODULES_HOME}/${UTILBIN_DIR}/tclsh" ]] || [[ ${opt_force} == 'yes' ]]; then - build Tcl - fi - - if [[ ! -e "${PMODULES_HOME}/lib/tcllib1.20" ]] || [[ ${opt_force} == 'yes' ]]; then - build tcllib - fi - - if [[ ! -e "${PMODULES_HOME}/libexec/modulecmd.bin" ]] || [[ ${opt_force} == 'yes' ]]; then - build modules - fi - rm -rf "${PMODULES_HOME}/include" - rm -rf "${PMODULES_HOME}/lib/"*.a - rm -rf "${PMODULES_HOME}/lib/"*.la - rm -rf "${PMODULES_HOME}/lib/bash" - rm -rf "${PMODULES_HOME}/lib/pkginfo" - rm -rf "${PMODULES_HOME}/man" - rm -rf "${PMODULES_HOME}/share" - echo "Done..." } #----------------------------------------------------------------------------- +# help for sub-command 'install' # -pmodules::help_install() { +build::help_install() { echo " Usage: $(basename $0) install [OPTION...] Install a new Pmodules version. Options: ---prefix=DIR - Root of the Pmodules environment installation. The root of the - installation must be either specified via this option or the - environment variable PMODULES_ROOT. If this option is used and - the PMODULES_ROOT is set, the directory specified with this - option will be used. +--install_root=DIR + Root of the Pmodules environment installation. Everything will be + installed in a directory hierarchy with 'DIR' as prefix. + The default is '${DEFAULT_INSTALL_ROOT}'. --debug Enable verbose/debug output. ---disable-cleanup ) - Do not cleanup the tmp directory after compilation and installation. - ---force | -f - Force compilation. - ---help +--help|-h|-? Print this help text. " 1>&2 std::die 1 "" } -pmodules::install() { - if [[ -v PMOULES_HOME ]]; then - local prefix="${PMODULES_HOME%%/Tools*}" - else - local prefix="${DEFAULT_PMODULES_ROOT}" - fi - local config_file='' - local opt_force='no' - +#----------------------------------------------------------------------------- +# sub-command 'install' +# +# Install Pmodules files. +# +build::install() { while (( $# > 0 )); do case $1 in --debug ) set -x ;; - --prefix ) - prefix="$2" - shift 1 - ;; - --prefix=* ) - prefix="${1#*=}" - ;; - -f | --force ) - opt_force='yes' + --install_root | --install_root=* ) + if [[ $1 == *=* ]]; then + INSTALL_ROOT="${1#*=}" + else + INSTALL_ROOT="$2" + shift 1 + fi ;; --help | -h | -\? ) - pmodules::help_install + build::help_install ;; -* ) - std::die 1 "$1: illegal option" + illegal_option 'install' "$1" ;; * ) - std::die 1 "$1: illegal argument to sub-command 'install'." + illegal_arg 'install' "$1" ;; esac shift 1 done - local config_file="${prefix}/${CONFIG_DIR}/${CONFIG_FILE}" - read_config_file "${config_file}" + : ${INSTALL_ROOT:=${DEFAULT_INSTALL_ROOT}} + PREFIX="${INSTALL_ROOT}/${PMOD_DIR}" + + read_config_file "${INSTALL_ROOT}/${CONFIG_FILE}" ### # # begin installation # echo "Configuration:" - echo " root of Pmodules environment: ${prefix}" - echo " Pmodule prefix: ${PMODULES_HOME}" - sed_cmd="s:@PMODULES_HOME@:${PMODULES_HOME}:g;" - sed_cmd+="s:@PMODULES_VERSION@:${PMODULES_VERSION}:g;" - sed_cmd+="s:@MODULES_VERSION@:${MODULES_VERSION}:g;" - sed_cmd+="s:@PMODULES_DISTFILESDIR@:${PMODULES_DISTFILESDIR}:g;" - sed_cmd+="s:@PMODULES_TMPDIR@:${PMODULES_TMPDIR}:g;" - sed_cmd+="s:@TCLSHDIR@:${PMODULES_HOME}/${UTILBIN_DIR}:g;" - sed_cmd+="s:@pager@::g;" - sed_cmd+="s:@pageropts@::g;" - sed_cmd+="s:@etcdir@:${PMODULES_ROOT}/${CONFIG_DIR}:g;" + echo " root of Pmodules environment: ${INSTALL_ROOT}" + echo " Pmodule prefix: ${PREFIX}" + sed_cmd+="s:@PMODULES_VERSION@:${VERSION}:g;" sed_cmd+="s:@VERSIONING@:#:g;" - sed_cmd+="s:@prefix@:${PMODULES_HOME}:g;" - sed_cmd+="s:@initdir@:${PMODULES_HOME}/init:g;" - sed_cmd+="s:@MODULES_RELEASE@:${PMODULES_VERSION}:g;" - sed_cmd+="s:@BASH@:${PMODULES_HOME}/${UTILBIN_DIR}/bash:g;" - sed_cmd+="s:@MODULECMD@:${PMODULES_HOME}/${UTILBIN_DIR}/modulecmd.bash:g;" - sed_cmd+="s:@MODMANAGE@:${PMODULES_HOME}/${UTILBIN_DIR}/modmanage.bash:g;" + sed_cmd+="s:@BASH@:${PREFIX}/${UTILBIN_DIR}/bash:g;" + sed_cmd+="s:@MODULECMD@:${PREFIX}/${UTILBIN_DIR}/modulecmd.bash:g;" + + sed "${sed_cmd}" "${SRC_DIR}/profile.bash.in" \ + > "${INSTALL_ROOT}/${CONFIG_DIR}/profile.bash-${VERSION}" + sed "${sed_cmd}" "${SRC_DIR}/profile.csh.in" \ + > "${INSTALL_ROOT}/${CONFIG_DIR}/profile.csh-${VERSION}" + sed "${sed_cmd}" "${SRC_DIR}/profile.zsh.in" \ + > "${INSTALL_ROOT}/${CONFIG_DIR}/profile.zsh-${VERSION}" + chmod 0644 "${INSTALL_ROOT}/${CONFIG_DIR}"/*-${VERSION} + + test -e "${INSTALL_ROOT}/${CONFIG_DIR}/profile.bash" || \ + install -m 0644 "$_-${VERSION}" "$_" + + test -e "${INSTALL_ROOT}/${CONFIG_DIR}/profile.csh" || \ + install -m 0644 "$_-${VERSION}" "$_" + + test -e "${INSTALL_ROOT}/${CONFIG_DIR}/profile.zsh" || \ + install -m 0644 "$_-${VERSION}" "$_" + + sed "${sed_cmd}" "${SRC_DIR}/modulecmd.in" \ + > "${PREFIX}/bin/modulecmd" + chmod 0755 "${PREFIX}/bin/modulecmd" + sed "${sed_cmd}" "${SRC_DIR}/modulecmd.bash.in" \ + > "${PREFIX}/libexec/modulecmd.bash" + chmod 0755 "${PREFIX}/libexec/modulecmd.bash" + sed "${sed_cmd}" "${SRC_DIR}/modulecmd.tcl.in" \ + > "${PREFIX}/libexec/modulecmd.tcl" + chmod 0755 "${PREFIX}/libexec/modulecmd.tcl" + + sed "${sed_cmd}" "${SRC_DIR}/libpmodules.bash.in" \ + > "${PREFIX}/lib/libpmodules.bash" + chmod 0755 "${PREFIX}/lib/libpmodules.bash" + + sed "${sed_cmd}" "${SRC_DIR}/modbuild.in" \ + > "${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}" - sed "${sed_cmd}" "${SRC_DIR}/profile.bash.in" > "${PMODULES_ROOT}/${CONFIG_DIR}/profile.bash-${PMODULES_VERSION}" - sed "${sed_cmd}" "${SRC_DIR}/profile.csh.in" > "${PMODULES_ROOT}/${CONFIG_DIR}/profile.csh-${PMODULES_VERSION}" - sed "${sed_cmd}" "${SRC_DIR}/profile.zsh.in" > "${PMODULES_ROOT}/${CONFIG_DIR}/profile.zsh-${PMODULES_VERSION}" - chmod 0644 "${PMODULES_ROOT}/${CONFIG_DIR}"/*-${PMODULES_VERSION} + install -m 0755 "${SRC_DIR}/yq.$(uname -m)_$(uname -s)" "${PREFIX}/libexec/yq" + install -m 0644 "${SRC_DIR}/bash" "${PREFIX}/init" + install -m 0644 "${SRC_DIR}/bash_completion" "${PREFIX}/init" + install -m 0644 "${SRC_DIR}/csh" "${PREFIX}/init" + install -m 0644 "${SRC_DIR}/zsh" "${PREFIX}/init" - test -e "${PMODULES_ROOT}/${CONFIG_DIR}/profile.bash" || \ - install -m 0644 "$_-${PMODULES_VERSION}" "$_" - - test -e "${PMODULES_ROOT}/${CONFIG_DIR}/profile.csh" || \ - install -m 0644 "$_-${PMODULES_VERSION}" "$_" - - test -e "${PMODULES_ROOT}/${CONFIG_DIR}/profile.zsh" || \ - install -m 0644 "$_-${PMODULES_VERSION}" "$_" - - sed "${sed_cmd}" "${SRC_DIR}/modulecmd.in" > "${PMODULES_HOME}/bin/modulecmd" - chmod 0755 "${PMODULES_HOME}/bin/modulecmd" - sed "${sed_cmd}" "${SRC_DIR}/modulecmd.bash.in" > "${PMODULES_HOME}/libexec/modulecmd.bash" - chmod 0755 "${PMODULES_HOME}/libexec/modulecmd.bash" - sed "${sed_cmd}" "${SRC_DIR}/modulecmd.tcl.in" > "${PMODULES_HOME}/libexec/modulecmd.tcl" - chmod 0755 "${PMODULES_HOME}/libexec/modulecmd.tcl" - - sed "${sed_cmd}" "${SRC_DIR}/libpmodules.bash.in" > "${SRC_DIR}/libpmodules.bash" - chmod 0755 "${SRC_DIR}/libpmodules.bash" - - sed "${sed_cmd}" "${SRC_DIR}/libpmodules.bash.in" > "${PMODULES_HOME}/lib/libpmodules.bash" - chmod 0755 "${PMODULES_HOME}/lib/libpmodules.bash" - - sed "${sed_cmd}" "${SRC_DIR}/modbuild.in" > "${PMODULES_HOME}/bin/modbuild" - chmod 0755 "${PMODULES_HOME}/bin/modbuild" - - sed "${sed_cmd}" "${SRC_DIR}/modmanage.in" > "${PMODULES_HOME}/bin/modmanage" - chmod 0755 "${PMODULES_HOME}/bin/modmanage" - sed "${sed_cmd}" "${SRC_DIR}/modmanage.bash.in" > "${PMODULES_HOME}/libexec/modmanage.bash" - chmod 0755 "${PMODULES_HOME}/libexec/modmanage.bash" - - test -e "${PMODULES_ROOT}/${CONFIG_DIR}/Pmodules.yaml" || \ - install -m 0644 "$_" "${PMODULES_ROOT}/${CONFIG_DIR}" - - install -m 0755 "${SRC_DIR}/yq.$(uname -m)_$(uname -s)" "${PMODULES_HOME}/libexec/yq" - install -m 0644 "${SRC_DIR}/bash" "${PMODULES_HOME}/init" - install -m 0644 "${SRC_DIR}/bash_completion" "${PMODULES_HOME}/init" - install -m 0644 "${SRC_DIR}/csh" "${PMODULES_HOME}/init" - install -m 0644 "${SRC_DIR}/zsh" "${PMODULES_HOME}/init" - - install -m 0644 "${SRC_DIR}/libpbuild.bash" "${PMODULES_HOME}/lib" - install -m 0644 "${SRC_DIR}/libpbuild_dyn.bash" "${PMODULES_HOME}/lib" - install -m 0644 "${SRC_DIR}/libstd.bash" "${PMODULES_HOME}/lib" - install -m 0755 -d "${PMODULES_HOME}/lib/Pmodules" - install -m 0644 "${SRC_DIR}/libmodules.tcl" "${PMODULES_HOME}/lib/Pmodules" + install -m 0644 "${SRC_DIR}/libpbuild.bash" "${PREFIX}/lib" + install -m 0644 "${SRC_DIR}/libstd.bash" "${PREFIX}/lib" + install -m 0755 -d "${PREFIX}/lib/Pmodules" + install -m 0644 "${SRC_DIR}/libmodules.tcl" "${PREFIX}/lib/Pmodules" { - PATH="${PMODULES_HOME}/${UTILBIN_DIR}:${PATH}" - cd "${PMODULES_HOME}/lib/Pmodules" + PATH="${PREFIX}/${UTILBIN_DIR}:${PATH}" + cd "${PREFIX}/lib/Pmodules" "${BOOTSTRAP_DIR}/mkindex.tcl" } - install -m 0644 "${SRC_DIR}/modulefile" "${PMODULES_ROOT}/Tools/modulefiles/Pmodules/${PMODULES_VERSION}" + install -m 0644 \ + "${SRC_DIR}/modulefile" \ + "${INSTALL_ROOT}/Tools/modulefiles/Pmodules/${VERSION}" echo "Done..." } #============================================================================= # - -declare -a build_opts=() -build_opts+=( '--bootstrap' ) - declare subcmd='' declare -a subcmd_args=() @@ -550,7 +534,7 @@ done [[ -n "${subcmd}" ]] || std::die 1 "Missing sub-command.\n\nUse 'build --help' to get help..." -pmodules::${subcmd} "${subcmd_args[@]}" +build::${subcmd} "${subcmd_args[@]}" # Local Variables: # mode: sh diff --git a/config/modbuild.conf.in b/config/modbuild.conf.in deleted file mode 100644 index 6292ea6..0000000 --- a/config/modbuild.conf.in +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash -# -: ${PMODULES_DISTFILESDIR:=@PMODULES_DISTFILESDIR@} -: ${PMODULES_TMPDIR:=@PMODULES_TMPDIR@} -export PMODULES_DISTFILESDIR -export PMODULES_TMPDIR - -declare -x PMODULES_HOME="@PMODULES_ROOT@/Tools/Pmodules/${PMODULES_VERSION}" - -declare -x DefaultGroups='Tools Programming' -declare ReleaseStages=':unstable:stable:deprecated:' -declare DefaultReleaseStages='stable' - diff --git a/config/versions.conf b/config/versions.conf deleted file mode 100644 index e315957..0000000 --- a/config/versions.conf +++ /dev/null @@ -1,7 +0,0 @@ -bash 5.1.16 -findutils 4.9.0 -getopt 1.1.6 -modules 3.2.10.1 -Pmodules 1.1.8 -Tcl 8.6.12 -tcllib 1.20 diff --git a/doc/Overlays.adoc b/doc/Overlays.adoc new file mode 100644 index 0000000..b486cc6 --- /dev/null +++ b/doc/Overlays.adoc @@ -0,0 +1,117 @@ += Overlays +:TOC: +:sectnums: + +Please note: This document is work in progress! + +== What are Overlays and for what can they be used? + +== Overlay configuration + +=== System wide + +==== Example +.A system wide configuration file `/opt/psi/config/Pmodules.yaml` +==== +.... +DefaultGroups: Tools:Programming +DefaultReleaseStages: stable +ReleaseStages: unstable:stable:deprecated +TmpDir: /opt/psi/var/tmp/${USER} +DistfilesDir: /opt/psi/var/distfiles + +Overlays: + base: + install_root: /opt/psi + modulefiles_root: /opt/psi + devel: + install_root: /opt/psi + modulefiles_root: ${HOME}/modulefiles +.... +==== +==== YAML Format +.Format +==== +.... +Overlays: + : + install_root: + modulefiles_root: + type: + ... +.... +==== +``:: Name of overlay. Note: whitespace in the name is not supported. +`.install_root`:: The root of the software installation. This key is mandatory. +`.modulefiles_root`:: The root of the modulefile hierarchy. This key is optional and defaults to `.install_root> +`.type`:: The type of the overlay, see below. This key is optional and the default value is `n`. + +==== Overlay types +`n`:: Normal overlay. +`h`:: Hiding overlay. +`r`:: Replacing overlay. + +=== User defined overlays + +Each user can define his own overlays in `$HOME/.Pmodules/Pmodules.yaml`. + +== Working with overlays + +== Building modules + +The old format of the variants file is simple but very limited and almost impossible to extend for new features. To overcome the limitations a new format using YAML for variants files has been introduced. For the time being both format are supported. But it is highly recommended to use the YAML format for new modules and to migrate existing variants files in the old format to the new. + +=== With a YAML variants file + +==== Example of a variants file in YAML format +.A YAML variants file +==== +.... +overlay: base + +hdf5_serial/1.12.2: +- with: gcc/{5.5.0,6.5.0,7.5.0,10.2.0,10.3.0} + relstage: stable +- with: gcc/{8.5.0,9.5.0,11.3.0,12.1.0} + relstage: unstable + overlay: devel +.... +==== + +==== Format specification +.YAML format +.... +relstage: +overlay: +systems: + +/: + - with: + dependencies: + relstate: + overlay: + systems: + ... +... +.... +==== Defaults +Default values can be overriden per version/variant. + +`overlay`:: The default overlay the module will be installed in. This value can be overriden for dedicated versions/variants. +`systems`:: The default for supported systems. + +==== Versions and Variants + +`/`:: An array with variants for this version. + +`/.[i].with`:: Hierarchical dependencies for variant `i`. + +`/.[i].dependencies`:: Build/run-time dependencies for variant `i`. + +`/.[i].relstage`:: Relase stage of variant `i`. + +`/.[i].overlay`:: Overlay of variant `i`. + +`/.[i].systems`:: Supported systems. + +=== Legacy format diff --git a/recipes/010-bash b/recipes/010-bash new file mode 100755 index 0000000..4956448 --- /dev/null +++ b/recipes/010-bash @@ -0,0 +1,42 @@ +#!/bin/bash +# +# https://www.gnu.org/software/bash/ +# +P=bash +V=${BASH5_VERSION:-5.1.16} +FNAME="$P-$V.tar.gz" +DOWNLOAD_URL="https://ftp.gnu.org/gnu/$P/${FNAME}" + +source "$(dirname "$0")/librecipes.bash" + +#--- +# configure +mkdir -p "${BUILD_DIR}" && cd "$_" || exit ${PB_ERR_SYSTEM} +loadablesdir="${PREFIX}/${UTILBIN_DIR}/builtins" \ +"${SRC_DIR}/configure" \ + --prefix="${PREFIX}" \ + --bindir="${PREFIX}/${UTILBIN_DIR}" \ + || exit ${PB_ERR_CONFIGURE} + +#--- +# compile +make -j ${NJOBS} || exit ${PB_ERR_MAKE} +make -C examples/loadables -j ${NJOBS} || exit ${PB_ERR_MAKE} + +#--- +# install +make install || exit ${PB_ERR_INSTALL} + +#--- +# post-install +rm -rf "${PREFIX}/include/bash" +rm -rf "${PREFIX}/share/locale" +rm -rf "${PREFIX}/share/doc" +rm -rf "${PREFIX}/share/info" +rm -rf "${PREFIX}/share/man/man1/bash"* + +#--- +# Local Variables: +# mode: shell-script-mode +# sh-basic-offset: 8 +# End: diff --git a/recipes/020-tcl b/recipes/020-tcl new file mode 100755 index 0000000..0e380e0 --- /dev/null +++ b/recipes/020-tcl @@ -0,0 +1,60 @@ +#!/bin/bash +# +# https://www.tcl.tk +# +P=tcl +V=${TCL_VERSION:-8.6.12} +FNAME="$P$V-src.tar.gz" +DOWNLOAD_URL="https://prdownloads.sourceforge.net/tcl/${FNAME}" + +source "$(dirname "$0")/librecipes.bash" + +#--- +# download +test -r "${SRC_FILE}" || curl -L --output "$_" "${DOWNLOAD_URL}" || exit ${PB_ERR_DOWNLOAD} + +#--- +# unpack +mkdir -p "${SRC_DIR}" && cd "$_" || exit ${PB_ERR_SYSTEM} +tar --directory "${SRC_DIR}" --strip-components 1 -xv -f "${SRC_FILE}" || exit ${PB_ERR_UNTAR} + +#--- +# configure +mkdir -p "${BUILD_DIR}" && cd "$_" || exit ${PB_ERR_SYSTEM} +case $(uname -s) in +Linux ) + srcdir="${SRC_DIR}/unix" + ;; +Darwin ) + srcdir="${SRC_DIR}/macosx" + ;; +* ) + echo "Oops: unsupported OS!" 1>&2 + exit ${PB_ERR_SYSTEM} + ;; +esac +"${srcdir}"/configure \ + --prefix="${PREFIX}" \ + --bindir="${PREFIX}/${UTILBIN_DIR}" \ + --mandir="${PREFIX}/share/man" \ + --enable-shared=no \ + || exit ${PB_ERR_CONFIGURE} + +#--- +# compile & install +make -j ${NJOBS} || exit ${PB_ERR_MAKE} +make install || exit ${PB_ERR_INSTALL} + +#--- +# post-install +{ cd "${PREFIX}/${UTILBIN_DIR}" && rm -f tclsh && ln -fs tclsh${V%.*} tclsh; }; + +rm -rf "${PREFIX}/share/man/man1/tclsh.1" +rm -rf "${PREFIX}/share/man/man3" +rm -rf "${PREFIX}/share/man/mann" + +#--- +# Local Variables: +# mode: shell-script-mode +# sh-basic-offset: 8 +# End: diff --git a/recipes/030-tcllib b/recipes/030-tcllib new file mode 100755 index 0000000..6497227 --- /dev/null +++ b/recipes/030-tcllib @@ -0,0 +1,48 @@ +#!/bin/bash +# +# https://core.tcl-lang.org +# +P=tcllib +V=${TCLLIB_VERSION:-1.21} +FNAME="$P-$V.tar.gz" +DOWNLOAD_URL="https://core.tcl-lang.org/tcllib/uv/${FNAME}" + +source "$(dirname "$0")/librecipes.bash" + +#--- +# download +test -r "${SRC_FILE}" || curl -L --output "$_" "${DOWNLOAD_URL}" || exit ${PB_ERR_DOWNLOAD} + +#--- +# unpack +mkdir -p "${SRC_DIR}" && cd "$_" || exit ${PB_ERR_SYSTEM} +tar --directory "${SRC_DIR}" --strip-components 1 -xv -f "${SRC_FILE}" || exit ${PB_ERR_UNTAR} +# configure +mkdir -p "${BUILD_DIR}" && cd "$_" || exit ${PB_ERR_SYSTEM} +"${SRC_DIR}"/configure \ + --prefix="${PREFIX}" \ + --mandir="${PREFIX}/share/man" \ + || exit ${PB_ERR_CONFIGURE} + +#--- +# compile & install +make -j ${NJOBS} || exit ${PB_ERR_MAKE} +make install || exit ${PB_ERR_INSTALL} + +#--- +# post-install +rm -rf "${PREFIX}/share/man/mann" +rm -f "${PREFIX}/bin/dtplite" +rm -f "${PREFIX}/bin/mkdoc" +rm -f "${PREFIX}/bin/nns" +rm -f "${PREFIX}/bin/nnsd" +rm -f "${PREFIX}/bin/nnslog" +rm -f "${PREFIX}/bin/page" +rm -f "${PREFIX}/bin/pt" +rm -f "${PREFIX}/bin/tcldocstrip" + +#--- +# Local Variables: +# mode: shell-script-mode +# sh-basic-offset: 8 +# End: diff --git a/recipes/040-modules b/recipes/040-modules new file mode 100755 index 0000000..5c8dca5 --- /dev/null +++ b/recipes/040-modules @@ -0,0 +1,59 @@ +#!/bin/bash +# +# https://core.tcl-lang.org +# +P=modules +V=${MODULES_VERSION:-3.2.10.1} +FNAME="$P-$V.tar.gz" +DOWNLOAD_URL="https://amas.web.psi.ch/Downloads/$P/$P-$V.tar.bz2" + +source "$(dirname "$0")/librecipes.bash" + +#--- +# configure +mkdir -p "${BUILD_DIR}" && cd "$_" || exit ${PB_ERR_SYSTEM} +case $(uname -s) in + Linux ) + declare -x LIBS="-lz -lpthread" + ;; + Darwin ) + declare -x LIBS="-lz -framework CoreFoundation" + ;; + * ) + echo "Oops: unsupported OS!" 1>&2 + exit ${PB_ERR_SYSTEM} + ;; +esac +CPPFLAGS="-DUSE_INTERP_ERRORLINE" \ +"${SRC_DIR}"/configure \ + --prefix="${PREFIX}" \ + --exec-prefix="${PREFIX}" \ + --with-module-path="${PREFIX%%/Tools*}/Tools/${PMODULES_MODULEFILES_DIR}" \ + --with-tcl="${PREFIX}/lib" \ + --without-x \ + --disable-versioning \ + || exit ${PB_ERR_CONFIGURE} + +#--- +# compile & install +make -j ${NJOBS} || exit ${PB_ERR_MAKE} +make install || exit ${PB_ERR_INSTALL} + +#--- +# post-install +mkdir -p "${PREFIX}/share/man/man1" +mkdir -p "${PREFIX}/share/man/man4" +mv -v "${PREFIX}/Modules/share/man/man1/module.1" "${PREFIX}/share/man/man1" +mv -v "${PREFIX}/Modules/share/man/man4/modulefile.4" "${PREFIX}/share/man/man4" +mkdir -p "${PREFIX}/libexec" +cp -v "${BUILD_DIR}/modulecmd" "${PREFIX}/libexec/modulecmd.bin" || exit 1 + +rm -rf "${PREFIX}/Modules" +rm -f "${PREIX}"/init/{ksh,perl.pm,python.py,ruby.rb,cmake,.modulespath} + +#--- +# Local Variables: +# mode: sh +# sh-basic-offset: 8 +# tab-width: 8 +# End: diff --git a/recipes/100-getopt b/recipes/100-getopt new file mode 100755 index 0000000..17979c8 --- /dev/null +++ b/recipes/100-getopt @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +# +# https://frodo.looijaard.name/project/getopt +# +P=getopt +V=${GETOPT_VERSION:-1.1.6} +FNAME="$P-$V.tar.gz" +DOWNLOAD_URL="http://frodo.looijaard.name/system/files/software/$P/$P-$V.tar.gz" + +#--- +# build on macOS only +[[ $(uname -s) == 'Darwin' ]] || exit 0 + +#--- +source "$(dirname "$0")/librecipes.bash" + +#--- +# configure +# nothing to configure but we need gettext from Macports +if [[ ! -d '/opt/local/bin' ]] || [[ ! -x '/opt/local/bin/msgfmt' ]]; then + echo "gettext port from Macports is required to build 'getopt'!" 1>&2 + exit 1 +fi + +#--- +# compile +PATH+=':/opt/local/bin' +declare -x C_INCLUDE_PATH="${PREFIX}/include:/opt/local/include" +declare -x LDFLAGS="/opt/local/lib/libintl.a /opt/local/lib/libiconv.a -framework CoreFoundation" +declare -x LIBRARY_PATH="${PREFIX}/lib" + +make -e all || exit 1 + +#--- +# install +declare -x DESTDIR="${PREFIX}" +declare -x prefix='' +#PATH="${PREFIX}/${UTILBIN_DIR}:${PATH}" +make -e install + +#--- +# post-install +mv "${PREFIX}/bin/getopt" "${PREFIX}/${UTILBIN_DIR}" +rm -rf "${PREFIX}/man" +rm -rf "${PREFIX}/share/locale" + +#--- +# Local Variables: +# mode: sh +# sh-basic-offset: 8 +# tab-width: 8 +# End: diff --git a/recipes/110-findutils b/recipes/110-findutils new file mode 100755 index 0000000..3f2ef6c --- /dev/null +++ b/recipes/110-findutils @@ -0,0 +1,43 @@ +#!/bin/bash +# +# https://www.gnu.org/software/findutils/ +# +P=findutils +V=${FINDUTILS_VERSION:-4.9.0} +FNAME="$P-$V.tar.xz" +DOWNLOAD_URL="https://ftp.gnu.org/gnu/$P/${FNAME}" + +#--- +# build on macOS only +[[ $(uname -s) == 'Darwin' ]] || exit 0 + +#--- +source "$(dirname "$0")/librecipes.bash" + +#--- +# configure +mkdir -p "${BUILD_DIR}" && cd "$_" || exit ${PB_ERR_SYSTEM} +loadablesdir="${PREFIX}/${UTILBIN_DIR}/builtins" \ +"${SRC_DIR}/configure" \ + --prefix="${PREFIX}" \ + --bindir="${PREFIX}/${UTILBIN_DIR}" \ + || exit ${PB_ERR_CONFIGURE} + +#--- +# compile & install +make -j ${NJOBS} || exit ${PB_ERR_MAKE} +make install || exit ${PB_ERR_INSTALL} + +#--- +# post-install +rm -vf "${PREFIX}/share/man/man5/locatedb.5" +rm -vf "${PREFIX}/share/man/man1/updatedb.1" +rm -vf "${PREFIX}/share/man/man1/xargs.1" +rm -vf "${PREFIX}/share/man/man1/locate.1" +rm -vf "${PREFIX}/share/man/man1/find.1" + +#--- +# Local Variables: +# mode: shell-script-mode +# sh-basic-offset: 8 +# End: diff --git a/recipes/librecipes.bash b/recipes/librecipes.bash new file mode 100644 index 0000000..c696aaf --- /dev/null +++ b/recipes/librecipes.bash @@ -0,0 +1,91 @@ +#!/bin/bash + +set -x +set -o errexit +set -o pipefail +shopt -s nullglob + +if (( $# == 0 )); then + echo "Usage: $0 " 1>&2 + exit 1 +fi + +PREFIX="$1" +if [[ ! -d ${PREFIX} ]]; then + echo "Destinstion directory '${PREFIX}' does not exist! Aborting..." 1>&2 + exit 2 +fi +TMP_DIR="${PMODULES_TMPDIR:-/var/tmp/${USER}}" +DOWNLOADS_DIR="${PMODULES_DISTFILESDIR:-${TMP_DIR}/Downloads}" +SRC_DIR="${TMP_DIR}/$P-$V/src" +BUILD_DIR="${TMP_DIR}/$P-$V/build" +SRC_FILE="${DOWNLOADS_DIR}/${FNAME}" + + +declare -ix PB_ERR_ARG=1 +declare -ix PB_ERR_SETUP=2 +declare -ix PB_ERR_SYSTEM=3 +declare -ix PB_ERR_DOWNLOAD=4 +declare -ix PB_ERR_UNTAR=5 +declare -ix PB_ERR_CONFIGURE=6 +declare -ix PB_ERR_MAKE=7 +declare -ix PB_ERR_PRE_INSTALL=8 +declare -ix PB_ERR_INSTALL=9 +declare -ix PB_ERR_POST_INSTALL=10 +declare -ix PB_ERR=255 +declare -ix NJOBS=4 + +pb_exit() { + local -i ec=$? + if [[ -n "${BASH_VERSION}" ]]; then + local -i n=${#BASH_SOURCE[@]} + local -r recipe_name="${BASH_SOURCE[n]}" + else + local -r recipe_name="${ZSH_ARGZERO}" + fi + echo -n "${recipe_name}: " + if (( ec == 0 )); then + echo "done!" + elif (( ec == PB_ERR_ARG )); then + echo "argument error!" + elif (( ec == PB_ERR_SETUP )); then + echo "error in setting everything up!" + elif (( ec == PB_ERR_SYSTEM )); then + echo "unexpected system error!" + elif (( ec == PB_ERR_DOWNLOAD )); then + echo "error in downloading the source file!" + elif (( ec == PB_ERR_UNTAR )); then + echo "error in un-taring the source file!" + elif (( ec == PB_ERR_CONFIGURE )); then + echo "error in configuring the software!" + elif (( ec == PB_ERR_MAKE )); then + echo "error in compiling the software!" + elif (( ec == PB_ERR_PRE_INSTALL )); then + echo "error in pre-installing the software!" + elif (( ec == PB_ERR_INSTALL )); then + echo "error in installing the software!" + elif (( ec == PB_ERR_POST_INSTALL )); then + echo "error in post-installing the software!" + else + echo "oops, unknown error!!!" + fi + exit ${ec} +} +#export -f pb_exit > /dev/null +trap "pb_exit" EXIT + +#--- +# download +mkdir -p "${DOWNLOADS_DIR}" || exit ${PB_ERR_SYSTEM} +test -r "${SRC_FILE}" || curl -L --output "$_" "${DOWNLOAD_URL}" || exit ${PB_ERR_DOWNLOAD} + +#--- +# unpack +mkdir -p "${SRC_DIR}" && cd "$_" || exit ${PB_ERR_SYSTEM} +tar --directory "${SRC_DIR}" --strip-components 1 -xv -f "${SRC_FILE}" || exit ${PB_ERR_UNTAR} + +#--- +# Local Variables: +# mode: shell-script-mode +# sh-basic-offset: 8 +# End: