diff --git a/.gitignore b/.gitignore index 1c5135b..7843cb2 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ Downloads tmp tclIndex pbuild.log +libpmodules.bash *~ diff --git a/CHANGELOG.md b/CHANGELOG.md index dcf9fa7..ecee56c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,147 @@ # Changelog of Pmodules +## Version 1.1.10 (not yet tagged) +* **modulecmd** + * *User visible changes* + * New options for `module search`. + * With the option `--group` the search can be restricted to a + group. + * With the option `--newest` only the newest versions are + displayed. + * `find` as alias for the sub-command `search` added. + * Bugfix: the sub-commands `whatis` and `keyword|apropos` were + broken by design. + * Bugfix: after loading a `Pmodules` module, it was not shown with + `module list`. + * Bugfix in scanning the depth of groups. + * Bugfix: after `module purge` the environment variable + `PMODULES_HOME` was not defined an more. + * Bugfix: source the shell init file only if a `Pmodules` module + is loaded. + * Bugfix: unsetting aliases in modulefiles was not handled + properly in `module purge` + * *Internal changes and fixes* + * initialisation error for bash and zsh fixed + +* **build-system** + * *User visible changes* + * `modbuild` is now defined as function like `module`. Therefor no + `Pmodules` module must be loaded to build a module with `modbuild` + * The system can now be defined in the module (YAML) configuration + file. + * Build dependencies can (and should) now be specified with + `build_requires` in the YAML configuration file. + * Bugfix: cleanup of modulefiles in overlays fixed. A module can be in + more than one overlay. These overlays must be specified in the + module configuration file. + * Bugfix: querying dependencies from YAML configuration file + fixed. Under some conditions the string 'null' was in the list + of dependencies. + * Bugfix: create group directory if it doesn't exist. + * Bugfix: create the module `$PREFIX` before processing the + install targets not before all targets. If `$PREFIX` is created + before processing any target and the build fails, `modbuild` + assumes that the module have been already built successfully. + * *Internal changes and fixes* + * code review/re-factoring + * `modbuild` is now using the Bash installed in `Pmodules` itself. + * test code with `set -o nounset`, several issues with this + setting fixed (not necessarily bugs). + +* **other changes** + * The build script to bootstrap Pmodules itself doesn't use modbuild + any more to compile required software packages. With this change + we can remove some special cases from modbuild. + * The bootstrap script requires Bash 5.0 or newer now. + * Bugfix: in the `Pmodules` modulefile force the sourcing of the + shell init script while in mode `load` only. + +## 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. + * The function `pbuild::supported_os` has been + removed. `pbuild::supported_systems` provides the same + functionality for legacy configuration files. In YAML module + configuration files `systems` have to be used. + +* **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 +* **buid-system** + * overlay definition must now be in YAML format + * support for YAML formatted variant files (the legacy format + is still supported) + * build-system in 1.1.6 was still work in progress and broken + +## Version 1.1.6 +* **modulecmd** + * bugfix in searching/loading modules in a hierarchical + group + +## Version 1.1.5 +* first public version with the overlay feature + ## Version 1.0.0rc11 * **modulecmd** * *User visible changes* diff --git a/Pmodules/bash b/Pmodules/bash index 5afa1be..4b826b1 100644 --- a/Pmodules/bash +++ b/Pmodules/bash @@ -11,10 +11,6 @@ fi ############################################################################ # some sanity checks # -if [[ ! -d ${PMODULES_ROOT} ]]; then - echo "Oops: ${PMODULES_ROOT}: Set as Pmodules root, but this is not a directory." 1>&2 - return 1 -fi if [[ ! -d ${PMODULES_HOME} ]]; then echo "Oops: ${PMODULES_HOME}: Set as Pmodules home, but this is not a directory." 1>&2 return 1 @@ -33,13 +29,9 @@ fi unset MODULE_VERSION unset MODULE_VERSION_STACK unset MODULESHOME -unset PMODULES_ENV -declare -x PMODULES_MODULEFILES_DIR='modulefiles' -declare -x PMODULES_CONFIG_DIR='config' declare -x PMODULES_DIR="${PMODULES_HOME}" - ############################################################################# # implement module comand as shell function # @@ -49,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 e8776a3..942ae33 100644 --- a/Pmodules/libmodules.tcl +++ b/Pmodules/libmodules.tcl @@ -4,6 +4,7 @@ # unload modules if parent removed # + if {[info exists env(PMODULES_DEBUG)] && $env(PMODULES_DEBUG)} { proc debug {msg} { set level [expr [info level] -2] @@ -24,13 +25,35 @@ debug "loading libmodules" package require base64 +set ::MODULEFILES_DIR "modulefiles" +set ::ol_replacing "r" + proc _pmodules_parse_pmodules_env { } { - debug "enter" + # + # In this library we need the value of some BASH variables + # defined in PMODULES_ENV. In this function we translate + # these variables definitions - created in BASH with + # 'typeset -p VAR' - to Tcl. + # foreach line [split [base64::decode $::env(PMODULES_ENV)] "\n"] { if { ![regexp -- {.* -[aAx]* (.*)=\((.*)\)} $line -> name value] } { continue } switch $name { + Dir2OverlayMap { + array set ::Dir2OverlayMap [regsub -all {[]=[]} $value " "] + } + OverlayInfo { + array set ::OverlayInfo [regsub -all {[]=[]} $value " "] + } + UsedOverlays { + array set tmp [regsub -all {[]=[]} $value " "] + set ::UsedOverlays {} + set l [lsort [array names tmp]] + foreach k $l { + lappend ::UsedOverlays $tmp($k) + } + } UsedGroups { set ::UsedGroups $value } @@ -55,14 +78,32 @@ proc module-addgroup { group } { set ::${group}_version $version if { [module-info mode load] } { - prepend-path MODULEPATH [file join \ - $::PmodulesRoot \ - $group \ - $::PmodulesModulfilesDir \ - {*}$::variant] + set overlays_to_add {} + foreach overlay $::UsedOverlays { + lappend overlays_to_add $overlay + set ol_type $::OverlayInfo($overlay:type) + debug "ol_type=$ol_type" + if { [string compare $ol_type $::ol_replacing] == 0 } { + break + } + } + foreach overlay [lreverse_n $overlays_to_add 1] { + debug "overlay=$overlay" + debug "group=$group" + debug "::variant=$::variant" + set dir [file join \ + $::OverlayInfo($overlay:mod_root) \ + $group \ + $::MODULEFILES_DIR \ + {*}$::variant] + debug "dir=$dir" + if { [file isdirectory $dir] } { + debug "prepend $dir to MODULEPATH " + prepend-path MODULEPATH $dir + } + } + debug "end foreach" prepend-path UsedGroups $group - debug "mode=load: new MODULEPATH=$env(MODULEPATH)" - debug "mode=load: new UsedGroups=$env(UsedGroups)" } elseif { [module-info mode remove] } { set GROUP [string toupper $group] debug "mode=remove: hierarchical group '${GROUP}'" @@ -83,14 +124,15 @@ proc module-addgroup { group } { debug "mode=remove: no orphan modules to unload" } debug "mode=remove: $env(MODULEPATH)" - set dir [file join \ - $::PmodulesRoot \ - $group \ - $::PmodulesModulfilesDir \ - {*}$::variant] - debug "mode=remove: dir=$dir" - remove-path MODULEPATH $dir - debug "mode=remove: $env(UsedGroups)" + foreach overlay $::UsedOverlays { + set dir [file join \ + $overlay \ + $group \ + $::MODULEFILES_DIR \ + {*}$::variant] + debug "remove $dir" + remove-path MODULEPATH $dir + } remove-path UsedGroups $group debug "mode=remove: $env(UsedGroups)" } @@ -131,7 +173,7 @@ proc _pmodules_setenv { PREFIX name version } { return } - set NAME [string toupper $name] + set NAME [string toupper $name] regsub -- "-" ${NAME} "_" NAME if { ! [info exist ::dont-setenv] } { @@ -154,6 +196,9 @@ proc _pmodules_setenv { PREFIX name version } { if { [lsearch ${::dont-setenv} "${NAME}_HOME"] == -1 } { setenv ${NAME}_HOME $PREFIX } + if { [lsearch ${::dont-setenv} "${NAME}_ROOT"] == -1 } { + setenv ${NAME}_ROOT $PREFIX + } } else { debug "$PREFIX is not a directory" } @@ -274,13 +319,45 @@ proc ModulesHelp { } { # intialize global vars # Modulefile is something like # -# ${PMODULES_ROOT}/group/${PMODULES_MODULEFILES_DIR}/name/version +# /group/modulefiles/name/version # or -# ${PMODULES_ROOT}/group/${PMODULES_MODULEFILES_DIR}/X1/Y1/name/version +# /group/modulefiles/X1/Y1/name/version # or -# ${PMODULES_ROOT}/group/${PMODULES_MODULEFILES_DIR}/X1/Y1//X2/Y2/name/version +# /group/modulefiles/X1/Y1//X2/Y2/name/version # +proc _find_overlay { modulefile_components } { + debug "_find_overlay()" + foreach ol $::UsedOverlays { + debug "$ol" + set ol_mod_root $::OverlayInfo(${ol}:mod_root) + if { [string range $ol_mod_root end end] == "/" } { + set ol_mod_root [string range $ol_mod_root 0 end-1] + } + 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_mod_root_splitted] - 1]]] + debug "$modulefile_root" + if { [string compare $ol_mod_root $modulefile_root] == 0 } { + debug "$ol_mod_root_splitted" + return $ol_mod_root_splitted + } + } + debug "not found" + return {} +} + +proc _is_in_overlay { } { + debug "_is_in_overlay?" + set parts [_find_overlay [file split $::ModulesCurrentModulefile]] + debug "_is_in_overlay: $parts" + expr {[string compare $parts ""] == 0 } +} + proc _pmodules_init_global_vars { } { + debug "_pmodules_init_global_vars() called" global group global GROUP global name @@ -292,38 +369,31 @@ proc _pmodules_init_global_vars { } { global V_PATCHLVL global V_RELEASE global V_PKG - global variant global PREFIX # prefix of package - debug "$::ModulesCurrentModulefile" - set ::PmodulesRoot $::env(PMODULES_ROOT) - set ::PmodulesModulfilesDir $::env(PMODULES_MODULEFILES_DIR) - set modulefile [file split $::ModulesCurrentModulefile] - set pmodules_root [file split $::PmodulesRoot] - set pmodules_root_num_dirs [llength $pmodules_root] + set modulefile_splitted [file split $::ModulesCurrentModulefile] - set modulefile_root [file join {*}[lrange $modulefile 0 \ - [expr $pmodules_root_num_dirs - 1]]] - if { $::PmodulesRoot != $modulefile_root } { - debug "stop sourcing: ${::PmodulesRoot} != $modulefile_root" - return - } - debug "modulefile is inside our root" - set rel_modulefile [lrange $modulefile [llength $pmodules_root] 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 end-1] + set name [lindex $modulefile_splitted end-1] set P "${name}" - set version [lindex $modulefile end] + set version [lindex $modulefile_splitted end] set V "${version}" lassign [split $V -] V_PKG tmp 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 prefix "$pmodules_root $group [lreverse_n $variant 2]" + set variant [lrange $rel_modulefile 2 end] + 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 $::OverlayInfo(${ol}:inst_root)] + set prefix "$install_prefix $group [lreverse_n $variant 2]" set PREFIX [file join {*}$prefix] - + debug "PREFIX=$PREFIX" debug "group of module $name: $group" } @@ -331,14 +401,13 @@ if { [info exists ::whatis] } { module-whatis "$whatis" } -_pmodules_init_global_vars - -# -# we cannot load another module with the same name -# -conflict $name - -_pmodules_setenv ${PREFIX} ${name} ${version} -_pmodules_update_loaded_modules ${group} ${name} ${version} - +_pmodules_parse_pmodules_env +if {[_is_in_overlay] == 0} { + debug "setup env vars for module in overlay" + _pmodules_init_global_vars + conflict $name + _pmodules_setenv ${PREFIX} ${name} ${version} + _pmodules_update_loaded_modules ${group} ${name} ${version} +} debug "return from lib" + diff --git a/Pmodules/libpbuild.bash b/Pmodules/libpbuild.bash index 6df73c3..39c6bea 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,23 +21,208 @@ 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 +# + +############################################################################### +# +# general functions +# + +#.............................................................................. +# +# Install module in given group. +# +# Note: +# This function is deprecated with YAML module configuration files. +# +# Arguments: +# $1: group +# +pbuild::add_to_group() { + if (( $# == 0 )); then + std::die 42 \ + "%s " "${module_name}/${module_version}:" \ + "${FUNCNAME}: missing group argument." + fi + if (( $# > 1 )); then + std::die 42 \ + "%s " "${module_name}/${module_version}:" \ + "${FUNCNAME}: only one argument is allowed." + fi + if [[ ${yaml_config} == 'yes' ]]; then + std::info \ + "Using ${FUNCNAME} is deprecated with YAML module configuration files." + fi + pbuild.add_to_group "$@" +} +readonly -f pbuild::add_to_group + +declare -gx GROUP='' +pbuild.add_to_group(){ + GROUP="$1" +} +readonly -f pbuild.add_to_group + +#.............................................................................. +# +# Test whether a module with the given name is available. If yes, return +# release +# +# Arguments: +# $1: module name +# $2: optional variable name to return release +# +# Notes: +# The passed module name must be module/version! +# +# Exit codes: +# 0 if module/version is available +# 1 otherwise +# +pbuild::module_is_avail() { + local output=( $("${MODULECMD}" bash avail -a -m "$1" \ + 2>&1 1>/dev/null) ) + local i + for (( i = 0; i < ${#output[@]}; i += 2 )); do + if [[ "${output[$i]}" == "$1" ]]; then + if (( $# > 1 )); then + local -n _result="$2" + _result="${output[i+1]}" + fi + return 0 + fi + done + return 1 +} +readonly -f pbuild::module_is_avail #.............................................................................. # # compare two version numbers # -# original implementation found on stackoverflow: +# pbuild::version_compare +# - returns 0 if the version numbers are equal +# - returns 1 if first version number is higher +# - returns 2 if second version number is higher +# +# pbuild::version_lt +# - returns 0 if second version number is higher +# pbuild::version_le +# - returns 0 if second version number is higher or equal +# pbuild::version_gt +# - returns 0 if first version number is higher +# pbuild::version_ge +# - returns 0 if first version number is higher or equal +# pbuild::version_eq +# - returns 0 if version numbers are equal +# +# otherwise a value != 0 is returned +# +# Arguments: +# $1 first version number +# $2 second version number +# +# Note: +# Original implementation found on stackoverflow: # https://stackoverflow.com/questions/4023830/how-to-compare-two-strings-in-dot-separated-version-format-in-bash # pbuild::version_compare () { @@ -81,158 +250,44 @@ pbuild::version_compare () { done return 0 } +readonly -f pbuild::version_compare pbuild::version_lt() { pbuild::version_compare "$1" "$2" (( $? == 2 )) } +readonly -f pbuild::version_lt pbuild::version_le() { pbuild::version_compare "$1" "$2" local -i exit_code=$? (( exit_code == 0 || exit_code == 2 )) } - +readonly -f pbuild::version_le pbuild::version_gt() { + pbuild::version_compare "$1" "$2" + (( $? == 1 )) + local -i exit_code=$? + (( exit_code == 1 )) +} +readonly -f pbuild::version_gt + +pbuild::version_ge() { pbuild::version_compare "$1" "$2" (( $? == 1 )) local -i exit_code=$? (( exit_code == 0 || exit_code == 1 )) } +readonly -f pbuild::version_gt pbuild::version_eq() { pbuild::version_compare "$1" "$2" } +readonly -f pbuild::version_eq #.............................................................................. # -# 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' - -############################################################################## -# -# Set flag to build module in source tree. -# -# Arguments: -# none -# -pbuild::compile_in_sourcetree() { - BUILD_DIR="${SRC_DIR}" -} - -############################################################################## -# -# Check whether the script is running on a supported OS. -# -# Arguments: -# $@: supported opertating systems (something like RHEL6, macOS10.14, ...). -# Default is all. -# -pbuild::supported_systems() { - SUPPORTED_SYSTEMS+=( "$@" ) -} - -############################################################################## -# -# Check whether the script is running on a supported OS. -# -# Arguments: -# $@: supported opertating systems (like Linux, Darwin). -# Default is all. -# -pbuild::supported_os() { - SUPPORTED_OS+=( "$@" ) -} - -############################################################################## -# # Check whether the loaded compiler is supported. # # Arguments: @@ -240,158 +295,193 @@ pbuild::supported_os() { # Default is all. # pbuild::supported_compilers() { + if [[ ${yaml_config} == 'yes' ]]; then + std::info \ + "Using ${FUNCNAME} is deprecated with YAML module configuration files." + fi + pbuild.supported_compilers "$@" +} +readonly -f pbuild::supported_compilers + +declare SUPPORTED_COMPILERS=() +pbuild.supported_compilers(){ SUPPORTED_COMPILERS+=( "$@" ) } +readonly -f pbuild.supported_compilers -############################################################################## +#.............................................................................. # -# Install module in given group. +# Check whether the script is running on a supported OS. # # Arguments: -# $1: group +# $@: supported opertating systems (something like RHEL6, macOS10.14, ...). +# Default is all. # -pbuild::add_to_group() { - if [[ -z ${1} ]]; then - std::die 42 \ - "%s " "${module_name}/${module_version}:" \ - "${FUNCNAME}: missing group argument." +pbuild::supported_systems() { + if [[ ${yaml_config} == 'yes' ]]; then + std::info \ + "Using ${FUNCNAME} is deprecated with YAML module configuration files." fi - GROUP="$1" + pbuild.supported_systems "$@" } +readonly -f pbuild::supported_systems + +declare SUPPORTED_SYSTEMS=() +pbuild.supported_systems() { + SUPPORTED_SYSTEMS+=( "$@" ) +} + +#.............................................................................. +# +pbuild::use_flag() { + [[ "${USE_FLAGS}" =~ ":${1}:" ]] +} +readonly -f pbuild::use_flag ############################################################################## # -# Set documentation file to be installed. +# functions to prepare the sources + +#.............................................................................. +# +# Set the download URL and name of downloaded file. # # Arguments: -# $@: documentation files relative to source -# -pbuild::install_docfiles() { - MODULE_DOCFILES+=("$@") -} - -############################################################################## -# -# Test whether a module with the given name is available. If yes, return -# release -# -# Arguments: -# $1: module name -# $2: optional variable name to return release via upvar -# -# Notes: -# The passed module name must be NAME/VERSION! -# -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]}" - return 0 - fi - done - return 1 -} - +# $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 -pbuild::use_cc() { - [[ -x "$1" ]] || std::die 3 \ - "%s " "${module_name}/${module_version}:" \ - "Error in setting CC:" \ - "'$1' is not an executable!" - CC="$1" +#.............................................................................. +# +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::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::unpack(){ + local -r file="$1" + local -r dir="${2:-${SRC_DIR}}" + ${tar} --directory="${dir}" -xv --strip-components 1 -f "${file}" } -pbuild::pre_prep() { - : -} - -pbuild::post_prep() { - : -} - -############################################################################### +#.............................................................................. # # extract sources. For the time being only tar-files are supported. # 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 +489,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 +513,16 @@ 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}" + { + mkdir -p "${dir}" + pbuild::unpack "${file}" "${dir}" + } || { + ${rm} -f "${file}" std::die 4 \ "%s " \ "${module_name}/${module_version}:" \ @@ -447,26 +539,24 @@ 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}:" \ "error patching sources!" done } - if [[ -z "${SOURCE_URLS}" ]]; then - for fname in ${VERSIONS[@]/#/pbuild::set_download_url_}; do - if typeset -F ${fname} 2>/dev/null; then - $f - break - fi - done - fi - [[ -z "${SOURCE_URLS}" ]] && \ + for fname in ${VERSIONS[@]/#/pbuild::set_download_url_}; do + if typeset -F ${fname} 2>/dev/null; then + $f + break + fi + done + (( ${#SOURCE_URLS[@]} == 0 )) && \ 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 +569,82 @@ 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_NAMES[i]}" + if [[ -v SOURCE_UNPACK_DIRS[${key}] ]]; then + echo "dir specified" + dir="${SOURCE_UNPACK_DIRS[${key}]}" + 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}" } ############################################################################### # +# functions to configure the sources + +#.............................................................................. # -pbuild::pre_configure() { - : -} - -pbuild::set_configure_args() { - CONFIGURE_ARGS+=( "$@" ) -} - 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 +#.............................................................................. +# +# Use this C-compiler +# +# Arguments: +# $1 C-compiler to use. +# +pbuild::use_cc() { + [[ -x "$1" ]] || std::die 3 \ + "%s " "${module_name}/${module_version}:" \ + "Error in setting CC:" \ + "'$1' is not an executable!" + CC="$1" +} +readonly -f pbuild::use_cc + +#.............................................................................. +# +# Set flag to build module in source tree. +# +# Arguments: +# none +# +pbuild::compile_in_sourcetree() { + BUILD_DIR="${SRC_DIR}" +} +readonly -f pbuild::compile_in_sourcetree + +#.............................................................................. +# +# Configure the software to be compiled. +# +# Arguments: +# none +# pbuild::configure() { case "${configure_with}" in autotools ) @@ -562,6 +676,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 +692,58 @@ pbuild::configure() { fi } -pbuild::post_configure() { - : -} -pbuild::pre_compile() { - : -} +############################################################################## +# +# functions to compile the sources +#.............................................................................. +# +# 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() { - : -} +############################################################################## +# +# functions to install everything -pbuild::pre_install() { - : +#.............................................................................. +# +# Set documentation file to be installed. +# +# Arguments: +# $@: documentation files relative to source +# +pbuild::install_docfiles() { + MODULE_DOCFILES+=("$@") } +readonly -f pbuild::install_docfiles +#.............................................................................. +# +# 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 +751,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 +772,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,638 +783,35 @@ pbuild::install_shared_libs() { esac } -pbuild::post_install() { - : -} - +############################################################################### # -# The 'do it all' function. +# The following two functions are the entry points 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 '/' \ - "${PMODULES_ROOT}/${GROUP}/${PMODULES_MODULEFILES_DIR}" \ - "${names[@]}" \ - "${module_name}") - modulefile_name="${modulefile_dir}/${module_version}" - PREFIX="${PMODULES_ROOT}/${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 -m0444 \ - "${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 -m0444 \ - "${BUILD_SCRIPT}" \ - "${target_dir}" - install -m0444 \ - "${modulefile}" \ - "${target_dir}" - #install -m 0755 \ - # -d "${target_dir}/files" - #install -m0444 \ - # "${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 ${PMODULES_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 0444 "${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 +declare yaml_config='yes' +pbuild.build_module_legacy(){ + yaml_config='no' + _build_module "$@" } +readonly -f pbuild.build_module_legacy -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_yaml(){ + _build_module "$@" } +readonly -f pbuild.build_module_yaml -pbuild.build_module() { - module_name="$1" - module_version="$2" - module_release="$3" +#.............................................................................. +# +# The real worker function. +# +_build_module() { + 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=() @@ -1285,66 +819,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 } #...................................................................... @@ -1352,13 +871,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 @@ -1420,74 +997,656 @@ 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" + + declare -g BUILD_ROOT="${PMODULES_TMPDIR}/${module_name}-${module_version}" + SRC_DIR="${BUILD_ROOT}/src" + BUILD_DIR="${BUILD_ROOT}/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 -g PREFIX='' + + SOURCE_URLS=() + SOURCE_SHA256_SUMS=() + SOURCE_NAMES=() + declare -Ag SOURCE_UNPACK_DIRS=() + CONFIGURE_ARGS=() + 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_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" || : + + # loop over version specific functions. In these function + # more MODULE_DOCFILES can be defined. + # :FIXME: maybe we find a better solution. + for f in ${VERSIONS[@]/#/pbuild::install_docfiles_}; do + if typeset -F "$f" 2>/dev/null; then + $f + break + fi + done + (( ${#MODULE_DOCFILES[@]} == 0 )) && return 0 + ${install} -m0644 \ + "${MODULE_DOCFILES[@]/#/${SRC_DIR}/}" \ + "${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 (( ${#runtime_dependencies[@]} > 0 )); then + write_runtime_dependencies \ + "${PREFIX}/${FNAME_RDEPS}" \ + "${runtime_dependencies[@]}" + fi + if (( ${#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? - eval $( "${MODULECMD}" bash use Libraries ) - eval $( "${MODULECMD}" bash use System ) - - 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_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="${PMODULES_ROOT}/${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 -} +readonly -f _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 99e09f5..30d2f25 100644 --- a/Pmodules/libpmodules.bash.in +++ b/Pmodules/libpmodules.bash.in @@ -7,9 +7,19 @@ declare -A Subcommands=() declare -A Options=() declare -A Help=() +declare -a Overlays=() +declare -A OverlayInfo +declare -a UsedOverlays +declare -A Dir2OverlayMap + +declare -r ol_normal='n' +declare -r ol_hiding='h' +declare -r ol_replacing='r' + + # initialize help text of 'module --version' Help['version']=" -Pmodules @PMODULES_VERSION@ using Tcl Environment Modules @MODULES_VERSION@ +Pmodules @PMODULES_VERSION@ using Tcl Environment Modules Copyright GNU GPL v2 " @@ -28,34 +38,147 @@ 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 root and compute group depth's +# (Re-)Scan available groups in given overlays and compute group depth's # # Args: -# $1: root of modulefile hierarchy +# $@: overlay names # scan_groups () { - local -r root="$1" - local moduledir - for moduledir in ${root}/*/${PMODULES_MODULEFILES_DIR}; do - compute_group_depth "${moduledir}" + local ol + local depth + for ol in "$@"; do + 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 +} + +pm::read_config(){ + local -a config_files=() + + _get_config_files(){ + # + # return array with overlay configuration files + # + # 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" \ + "$_" + fnames+=("${PMODULES_OVERLAYS_DEF}") + fi + + # user defined + if [[ -r "${HOME}/.Pmodules/Pmodules.yaml" ]]; then + fnames+=("${HOME}/.Pmodules/Pmodules.yaml") + fi + + # system config file + test -r "${PMODULES_HOME%%/Tools*}/config/Pmodules.yaml" || \ + std::die 3 \ + "%s %s -- %s" \ + "base overlay definition file" \ + "does not exist or is not readable" \ + "$_" + + fnames+=("${PMODULES_HOME%%/Tools*}/config/Pmodules.yaml") + } + + _get_ol_names(){ + # + # get the names of all overlays + # + local -n fnames="$1" + ${yq} -Ne eval-all '. as $item ireduce ({}; . *+ $item) |.Overlays|keys()' \ + "${fnames[@]}" | awk '{print $2}' + } + + _get_config_files config_files + + eval $(std::parse_yaml "${config_files[-1]}" 'cfg_') + [[ -v cfg_DefaultGroups ]] && DefaultGroups="${cfg_DefaultGroups}" + [[ -v cfg_DefaultReleaseStages ]] && DefaultReleaseStages="${cfg_DefaultReleaseStages}" + [[ -v cfg_ReleaseStages ]] && ReleaseStages="${cfg_ReleaseStages}" + unset ${!cfg_*} + + # + # loop over all overlays and save config in OverlayInfo and Dir2OverlayMap + # + # - at least install_root must be specified + # - modulefiles_root default so install_root + # - the type defaults to ${ol_normal} + # + Overlays=( $(_get_ol_names config_files) ) + local ol='' + for ol in "${Overlays[@]}"; do + eval $(${yq} -Ne eval-all \ + ". as \$item ireduce ({}; . *+ \$item) |.Overlays.${ol}" \ + "${config_files[@]}" | \ + sed 's/: /=/; s/\(.*\)/local \1/') + + [[ -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} ) + : + ;; + * ) + std::die 3 "Invalid type for overlay '${ol}' -- ${type}" + ;; + esac + OverlayInfo[${ol}:type]="${type}" + + OverlayInfo[${ol}:used]='no' + unset type modulefiles_root install_root done } + # Local Variables: # mode: sh # sh-basic-offset: 8 diff --git a/Pmodules/libstd.bash b/Pmodules/libstd.bash index 87e3b34..0b5121a 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,54 @@ 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 -r d=$2 + local -nr P="$1" + shift 1 - if ! echo ${!P} | egrep -q "(^|:)${d}($|:)" ; then - if [[ -z ${!P} ]]; then - export "$P=${d}" - else - export "$P=${d}:${!P}" - fi + 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 + P="${dirs}${P}" fi } std::remove_path() { - local -r P=$1 - local -r d=$2 + local -nr P="$1" + shift 1 + local -ar dirs="$@" local new_path='' local -r _P=( ${!P//:/ } ) - # loop over all entries in path - for entry in "${_P[@]}"; do - [[ "${entry}" != "${d}" ]] && new_path+=":${entry}" + local dir='' + for dir in "${dirs[@]}"; do + # loop over all entries in path + for entry in "${_P[@]}"; do + [[ "${entry}" != "${dir}" ]] && new_path+=":${entry}" + done done - # remove leading ':' - eval ${P}="${new_path:1}" + P="${new_path:1}" # remove leading ':' } # @@ -163,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 @@ -188,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 @@ -207,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 } @@ -274,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 @@ -309,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() { @@ -339,6 +354,29 @@ std::get_type() { esac } +std::parse_yaml() { + # + # parse a YAML file + # See: https://gist.github.com/pkuczynski/8665367 + # + local -r fname="$1" + local -r prefix="$2" + local s='[[:space:]]*' w='[a-zA-Z0-9_]*' fs=$(echo @|tr @ '\034') + sed -ne "s|^\($s\)\($w\)$s:$s\"\(.*\)\"$s\$|\1$fs\2$fs\3|p" \ + -e "s|^\($s\)\($w\)$s:$s\(.*\)$s\$|\1$fs\2$fs\3|p" "${fname}" | + awk -F$fs '{ + indent = length($1)/2; + vname[indent] = $2; + for (i in vname) { + if (i > indent) {delete vname[i]} + } + if (length($3) > 0) { + vn=""; + for (i=0; i&2; exit 3; } -# can be set in the configuration file -declare PMODULES_DISTFILESDIR='' -declare PMODULES_TMPDIR='' +############################################################################## +# +# 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 "${mydir}/../lib/libpmodules.bash" || \ std::die 3 "Oops: cannot source library -- '$_'" -# save arguments, (still) required for building dependencies -declare -r ARGS="$@" +############################################################################## +set -o nounset +shopt -s nocaseglob +shopt -s extglob +shopt -s nullglob ############################################################################## # @@ -80,7 +101,7 @@ BUILD-STEPS OPTIONS: --all Run throu all steps including cleanup. ---update-modulefiles +-update-modulefiles Only install the modulefile and set the release. MISCELLANEOUS OPTIONS: @@ -94,6 +115,9 @@ MISCELLANEOUS OPTIONS: -v | --verbose ) Verbose output. +--debug ) + Run in debug mode. + -j N | --jobs=N Run N parallel make jobs. @@ -119,11 +143,13 @@ MISCELLANEOUS OPTIONS: remove all files created during building. --distdir - Directory wwhere to store and lookup downloaded files. + Directory where to store and lookup downloaded files. --tmpdir Directory used for building a module. +--overlay + Install in this overlay. Defaults to '${PMODULES_HOME%%/Tools*}'. " exit 1 } @@ -136,10 +162,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='modbuild.conf' declare opt_build_target='all' declare opt_dry_run='no' declare opt_enable_cleanup_build='yes' @@ -148,9 +177,13 @@ 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_config_file='' + +declare BUILD_SCRIPT='' parse_args() { while (( $# > 0 )); do @@ -181,13 +214,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' @@ -208,51 +234,68 @@ 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 | --tmpdir=* ) + if [[ $1 == *=* ]]; then + PMODULES_TMPDIR="${1#--*=}" + else + PMODULES_TMPDIR="$2" + shift + fi ;; - --tmpdir ) - PMODULES_TMPDIR="$2" - shift + --system | --system=* ) + if [[ $1 == *=* ]]; then + opt_system="${1#--*=}" + else + opt_system="$2" + shift + fi ;; - --tmpdir=* ) - PMODULES_TMPDIR="${1/--tmpdir=}" + --overlay | --overlay=* ) + if [[ $1 == *=* ]]; then + opt_overlay="${1#--*=}" + else + opt_overlay="$2" + shift + fi ;; - --system ) - opt_system="$2" - shift + --use-flags | --use-flags=* ) + if [[ $1 == *=* ]]; then + USE_FLAGS=":${1#--*=}:" + else + USE_FLAGS=":$2:" + shift + fi ;; - --system=* ) - opt_system="${1/*=}" - ;; - --use-flags ) - USE_FLAGS="y:$2:" - shift - ;; - --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' ;; + --config-file | --config-file=* ) + if [[ $1 == *=* ]]; then + opt_config_file=( "${1#--*=}" ) + else + opt_config_file=( "$2" ) + shift + fi + ;; -- ) : ;; @@ -280,57 +323,72 @@ 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+=( '.*' ) } -shopt -s nocaseglob -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 - for f in "${BUILDBLOCK_DIR}"/*/"${BNAME_VARIANTS}"; do - [[ -e "${f}.${opt_system}" ]] \ - || [[ -e "${f}.$(uname -s)" ]] \ - || files+=( "$f" ) - done - (( nullglob_set == 1 )) && shopt -u nullglob - std::upvar "$1" "${files[@]}" +# +# bash brace expansion of given args. Input args like: +# +# "text" "gcc/{9.3.0,10.3.0}" "openmpi/{4.0.5,4.1.0}" +# +# will be expanded to the following four lines: +# +# "text gcc/9.3.0 openmpi/4.0.5" +# "text gcc/9.3.0 openmpi/4.1.0" +# "text gcc/10.3.0 openmpi/4.0.5" +# "text gcc/10.3.0 openmpi/4.1.0" +# +bash_expand(){ + local text="$1" + shift + local to_expand=( "${@}" ) + if (( ${#to_expand[@]} == 0 )); then + echo ${text} + else + local list + eval list=( ${to_expand[0]} ) + local s + for s in ${list[*]}; do + bash_expand "${text} ${s}" "${to_expand[@]:1}" + done; + fi; } -shopt -s extglob -expand_variants_file(){ - expand_deps(){ - local text="$1" - shift - local deps=( "${@}" ) - echo "${deps[@]}" - if (( ${#deps[@]} == 0 )); then - echo ${text} - else - local list - eval list=( ${deps[0]} ) - for dep in ${list[*]}; do - expand_deps "${text} ${dep}" "${deps[@]:1}" - done - fi +build_modules_legacy() { + find_variants_files(){ + local files=( "${BUILDBLOCK_DIR}"/*/"${BNAME_VARIANTS}"\.${opt_system} ) + files+=( "${BUILDBLOCK_DIR}"/*/"${BNAME_VARIANTS}.$(uname -s)" ) + local f + for f in "${BUILDBLOCK_DIR}"/*/"${BNAME_VARIANTS}"; do + [[ -e "${f}.${opt_system}" ]] \ + || [[ -e "${f}.$(uname -s)" ]] \ + || files+=( "$f" ) + done + std::upvar "$1" "${files[@]}" } - local -r input="$1" - while read -a toks; do - # skip empty and comment lines - [[ -z ${toks} ]] && continue - [[ ${toke:0:1} == '#' ]] && continue - # ignore first (module/version) and second (release) token - deps=( ${toks[*]:2} ) - expand_deps "${toks[0]} ${toks[1]}" "${deps[@]}" - done < "${input}" -} + expand_variants_file(){ + local -r input="$1" + while read -a toks; do + # skip empty and comment lines + [[ -z ${toks} ]] && continue + [[ ${toke:0:1} == '#' ]] && continue + local -a deps=( ${toks[*]:2} ) + bash_expand "${toks[0]} ${toks[1]}" "${deps[@]}" + done < "${input}" + } -build_modules() { local name="$1" local version="$2" local exact_match='no' @@ -350,25 +408,17 @@ build_modules() { 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 - local variants=() - local variants_files=() for f in "${files[@]}"; do local line='' while read line; do variants+=( "${line}" ) - variants_files+=( "$f" ) - done < <(expand_variants_file "${f}" | awk "${pattern}") - # here we should add a check, whether the version of the - # found variants are in the right variants files. Example: - # a variant for hdf5/1.10.4 is not allowed in a variants file - # for version 1.8. For this we need the mapping of a variant - # to the variants file in array 'variants_files' + done < <(expand_variants_file "${f}" | ${awk} "${pattern}") done if (( ${#variants[@]} == 0 )); then std::info "%s " \ @@ -383,6 +433,10 @@ build_modules() { done std::die 10 "Aborting..." fi + declare ol_name='base' + 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 @@ -391,20 +445,224 @@ build_modules() { version="${tokens[0]#*/}" release="${tokens[1]}" with_modules=( "${tokens[@]:2}" ) - pbuild.build_module \ + pbuild.build_module_legacy \ "${name}" "${version}" \ "${release}" "${with_modules[@]}" done } +build_modules_yaml(){ + local -A mod_overlays=() + local -- fname="${opt_config_file:-${BUILDBLOCK_DIR}/files/config.yaml}" + [[ -r "${fname}" ]] || \ + std::die 3 "YAML configuration file is not readable -- ${fname}" + echo "Using ${fname}..." + yaml_get_versions(){ + 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(){ + 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 -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' + } + + 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_group(){ + local -n _result="$1" + local fname="$2" + local version="$3" + local idx="$4" + _result=$(${yq} -Ne e ".\"${version}\"[${idx}].group" \ + "${fname}" 2>/dev/null) + (( $? == 0 )) && return + _result=$(${yq} -Ne e ".group" "${fname}" 2>/dev/null) + (( $? == 0 )) && return + _result='' + } + + 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=() + } + + yaml_get_compilers(){ + local -n _result="$1" + local fname="$2" + local version="$3" + local idx="$4" + _result=( $(${yq} -Ne e ".\"${version}\"[${idx}].compilers" \ + "${fname}" 2>/dev/null) ) + (( $? == 0 )) && return + _result=( $(${yq} -Ne e ".compilers" "${fname}" 2>/dev/null) ) + (( $? == 0 )) && return + _result=() + } + + 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=( $* ) + + local m + local pattern="//" + for m in "${with_modules[@]}"; do + pattern+=" && /${m//\//\\/}/" + done + + 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 group + local ol_name + for (( i=0; i 0 )); then + while read -a with_modules; do + pbuild.build_module_yaml \ + "${name}" "${v##*/}" \ + "${relstage}" "${with_modules[@]}" + done < <(bash_expand "" ${deps[@]}|${awk} "${pattern}") + else + pbuild.build_module_yaml \ + "${name}" "${v##*/}" \ + "${relstage}" + fi + done + done +} + +build_modules() { + if [[ -n $(ls "${BUILDBLOCK_DIR}/files/"*.yaml 2>/dev/null) ]]; then + build_modules_yaml "$@" + else + build_modules_legacy "$@" + fi +} + #............................................................................. # main parse_args "$@" -if [[ -z "${opt_system}" ]]; then - opt_system=$(std::get_os_release) -fi +opt_system="${opt_system:-$(std::get_os_release)}" pbuild.jobs "${opt_jobs}" pbuild.force_rebuild "${opt_force_rebuild}" @@ -416,20 +674,17 @@ pbuild.update_modulefiles "${opt_update_modulefiles}" pbuild.system "${opt_system}" pbuild.verbose "${opt_verbose}" -if [[ "${opt_bootstrap}" == 'yes' ]]; then - test -d "${BUILDBLOCK_DIR}/../../config" && PATH+=":$_" -else - # Please note: if we trap DEBUG a statement like - # test -d ... && PATH+=$_ - # does not work (at least on macOS with bash 4 and 5) - if [[ -d "${PMODULES_ROOT}/${PMODULES_CONFIG_DIR}" ]]; then - PATH+=":${PMODULES_ROOT}/${PMODULES_CONFIG_DIR}" - fi -fi -source "${opt_build_config}" || \ - std::die 3 "Oops: Cannot source configuration file -- '$_'" +# +# read configuration for modbuild +# +pm::read_config -: ${PMODULES_DISTFILESDIR:=${PMODULES_ROOT}/var/distfiles} +# :FIXME: should dist files go to +# ${pm_root}/var/distfiles +# or +# ${overlay}/var/distfiles +# ? +: ${PMODULES_DISTFILESDIR:="${PMODULES_HOME%%/Tools*}/var/distfiles"} : ${PMODULES_TMPDIR:=/var/tmp/${USER}} declare -r BUILD_SCRIPT @@ -439,17 +694,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 - 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 0d8bf26..b35fa96 100644 --- a/Pmodules/modulecmd.bash.in +++ b/Pmodules/modulecmd.bash.in @@ -1,42 +1,49 @@ #!@BASH@ --noprofile # +# +# which information do we need for overlays? +# - list of used overlays by name +# - dict to map an overlay name to install_root +# - dict to map an overlay name to modulefile_root +# - dict to map an overlay name to the overlay type +# - dict to map moduledirs to overlay name + unset CDPATH # unset CDPATH, otherwise 'cd' prints the directoy! unset IFS # use default IFS shopt -s nullglob -# used for some output only -declare -r CMD='module' +# used in some output messages only +declare -r CMD='module' -declare -r mydir=$(cd $(dirname "$0") && pwd) -declare prefix=$(dirname "${mydir}") -declare -r libdir="${prefix}/lib" -declare -r libexecdir="${prefix}/libexec" +declare -r mydir=$(cd $(dirname "$0") && pwd) +declare -- prefix=$(dirname "${mydir}") +declare -r libdir="${prefix}/lib" +declare -r libexecdir="${prefix}/libexec" source "${libdir}/libstd.bash" source "${libdir}/libpmodules.bash" -path="/bin:/usr/bin" -[[ $(uname -s) == 'Darwin' ]] && path+=":${libexecdir}" +declare -r os_name="$(uname -s)" + +path="${libexecdir}:/bin:/usr/bin" std::def_cmds "${path}" \ 'awk' 'base64' 'find' 'getopt' 'logger' 'mktemp' \ - 'rm' 'sort' 'find' + 'rm' 'sed' 'sort' 'yq' -if [[ ${PMODULES_PURETCL} == yes ]]; then - declare -r modulecmd="${libexecdir}/modulecmd.tcl" +if [[ -n ${TCLLIBPATH} ]]; then + declare -x TCLLIBPATH="${PMODULES_HOME}/lib/Pmodules:${TCLLIBPATH}" else - declare -rx TCLLIBPATH="${PMODULES_HOME}/lib/Pmodules" - declare -r modulecmd="${libexecdir}/modulecmd.bin" + declare -x TCLLIBPATH="${PMODULES_HOME}/lib/Pmodules" fi +declare -r modulecmd="${libexecdir}/modulecmd.bin" -declare verbosity_lvl=${PMODULES_VERBOSITY:-'verbose'} +declare -- verbosity_lvl=${PMODULES_VERBOSITY:-'verbose'} -declare Shell='' +declare -- Shell='' -declare -r pmodules_config_file="${PMODULES_ROOT}/${PMODULES_CONFIG_DIR}/Pmodules.conf" - -# the following settings are used if the Pmodules.conf doesn't exist +# the following settings are used if the config file doesn't exist # set groups which should be available after initialization declare -- DefaultGroups='Tools Programming' @@ -47,18 +54,22 @@ declare -- ReleaseStages=':unstable:stable:deprecated:' # set releases which should be available after initialization declare -- DefaultReleaseStages='stable' +# not used here but in modbuild +declare -- TmpDir="/opt/psi/var/tmp/${USER}" +declare -- DistfilesDir="/opt/psi/var/distfiles" + export_env() { case "${Shell}" in - sh | bash | zsh ) - local -r fmt="export %s=\"%s\"; " - ;; - csh | tcsh ) - local -r fmt="setenv %s \"%s\"; " - ;; - * ) - std::die 1 "Unsupported shell -- ${Shell}\n" - ;; + sh | bash | zsh ) + local -r fmt="export %s=\"%s\"; " + ;; + csh | tcsh ) + local -r fmt="setenv %s \"%s\"; " + ;; + * ) + std::die 1 "Unsupported shell -- ${Shell}" + ;; esac while (( $# > 0 )); do @@ -80,99 +91,173 @@ export_env() { } # -# Save/cache some variables. -# This function is called on exit via a trap handler. +# Save/cache state in the environment variable PMODULES_ENV. The content is +# base64 encoded. This function is called on exit via a trap handler. # -# Args; +# Arguments: # none # + declare g_env_must_be_saved='no' save_env() { + encode_base64(){ + case "${os_name}" in + Linux ) + "${base64}" --wrap=0 <<< "$1" + ;; + Darwin ) + # does not wrap if running in a script + "${base64}" <<< "$1" + ;; + * ) + std::die 255 "Oops: Unsupported OS" + ;; + esac + } + [[ $1 == 'no' ]] && return 0 local vars=( Version ) vars+=( UsedReleaseStages UsedFlags UsedGroups ) vars+=( DefaultGroups DefaultReleaseStages ) vars+=( ReleaseStages ) vars+=( GroupDepths ) + vars+=( Overlays ) + vars+=( UsedOverlays ) + vars+=( OverlayInfo Dir2OverlayMap) local s=$(typeset -p ${vars[@]}) - declare -g PMODULES_ENV=$( "${base64}" --wrap=0 <<< "$s" ) - export_env 'PMODULES_ENV' + declare -gx PMODULES_ENV=$( encode_base64 "$s" ) } +# +# function called on exit +# _exit() { save_env "${g_env_must_be_saved}" + export_env 'PMODULES_ENV' if [[ -n "${tmpfile}" ]] && [[ -e "${tmpfile}" ]]; then ${rm} -f "${tmpfile}" || : fi } - trap '_exit' EXIT # # get release stage of module # Note: -# - the release stage of a modulefile outside ${PMODULES_ROOT} is 'stable' -# - the release stage of a modulefile inside ${PMODULES_ROOT} without a -# coresponding file is 'unstable' +# - 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: -# $1: absolute modulefile name +# $1 upvar for returned release stage +# $2 modulefile directory (element of MODULEPATH) +# $3 module name/version # get_release_stage() { local "$1" - local -r modulefile="$2" + local -r dir="$2" + local -r modulefile="${dir}/$3" - # is modulefile outside ${PMODULES_ROOT}? - if [[ ! ${modulefile} =~ ${PMODULES_ROOT} ]]; then + if [[ ! -v Dir2OverlayMap[${dir%/${PMODULES_MODULEFILES_DIR}*}] ]]; then std::upvar $1 'stable' - return 0 + return fi - - # we are inside ${PMODULES_ROOT} + # + # In an overlay the name of the module-file is something like + # dir/modulefiles/name/version + # the corresponding file is + # dir/modulefiles/name/.release-version + # local -r rel_stage_file="${modulefile%/*}/.release-${modulefile##*/}" if [[ -r ${rel_stage_file} ]]; then # read file, remove empty lines, spaces etc - local -r data=$( < "${rel_stage_file}" ) - std::upvar $1 "${data}" + std::upvar $1 $( < "${rel_stage_file}" ) else std::upvar $1 'unstable' fi +} + +# +# check whether the argument in $1 is a valid release stage. +# +is_release_stage() { + [[ :${ReleaseStages}: =~ :$1: ]] +} + +# +# Check whether a given moduledir is in an used overlay. +# If yes, return 0 and the overlay with upvar of first argument +# otherwise return 1 +# +# Arguments +# $1 upvar to return overlay +# $2 upvar to return group +# $3 moduledir to check +# +find_overlay () { + local "$1" + 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}]}" + std::upvar $1 "${ol}" + + local group="${path#${OverlayInfo[${ol}:mod_root]}/}" + std::upvar $2 "${group}" return 0 } -is_release_stage() { - [[ ${ReleaseStages} =~ :$1: ]] +# +# Check whether the module passed in argument $1 is loaded. +# +module_is_loaded() { + [[ :${LOADEDMODULES}: =~ :$1: ]] } # -# Check whether argument is a group +# Check shebang. # -# Args: -# $1: string +# Arguments: +# $1 file name to test # -is_group () { - local -r group="$1" - # arg isn't emtpy and group already in cache - [[ -n ${group} ]] && [[ -n ${GroupDepths[${group}]} ]] && return 0 - local moduledir="${PMODULES_ROOT}/${group}/${PMODULES_MODULEFILES_DIR}" - [[ -d "${moduledir}" ]] || return 1 - compute_group_depth "${moduledir}" - g_env_must_be_saved='yes' -} - -# -# check shebang -# $1: file name to test is_modulefile() { local -r fname="$1" - local shebang + local -- shebang [[ -r ${fname} ]] || return 1 read -n 11 shebang < "${fname}" [[ "${shebang:0:8}" == '#%Module' ]] || [[ "${shebang:0:9}" == '#%Pmodule' ]] } +# +# Get the value of _PREFIX. +# +# Arguments: +# $1 upvar to return result +# $2 modulefile +# +get_module_prefix() { + local -n _prefix="$1" + local -- _prefix=$("${modulecmd}" bash show "$2" 2>&1 | \ + ${awk} '/_PREFIX |_HOME / {print $3; exit}') +} + +# +# Generic wrappers of 'modulecmd': +# +# subcommand_generic0: +# no argument allowed +# subcommand_generic1: +# Exact one argument must be passed +# subcommand_generic1plus: +# One or more arguments must be passed +# +# The options to output help are always accepted. +# subcommand_generic0() { local -r subcommand="$1" shift @@ -194,7 +279,7 @@ subcommand_generic0() { shift 1 done if (( ${#args[@]} > 0 )); then - std::die 3 "%s %s: %s\n" \ + std::die 3 "%s %s: %s" \ "${CMD}" "${subcommand}" \ "no arguments allowed" fi @@ -222,11 +307,11 @@ subcommand_generic1() { shift done if (( ${#args[@]} == 0 )); then - std::die 3 "%s %s: %s\n" \ + std::die 3 "%s %s: %s" \ "${CMD}" "${subcommand}" \ "missing argument" elif (( ${#args[@]} > 1 )); then - std::die 3 "%s %s: %s\n" \ + std::die 3 "%s %s: %s" \ "${CMD}" "${subcommand}" \ "only one argument allowed" fi @@ -254,7 +339,7 @@ subcommand_generic1plus() { shift done if (( ${#args[@]} == 0 )); then - std::die 3 "%s %s: %s\n" \ + std::die 3 "%s %s: %s" \ "${CMD}" "${subcommand}" \ "missing argument" fi @@ -287,11 +372,7 @@ subcommand_load() { local prefix='' local m='' - IFS=':' - local -a modulepath=(${MODULEPATH}) - unset IFS - - # + #...................................................................... # Test whether a given module is available. # The passed module-name can be # @@ -344,7 +425,7 @@ subcommand_load() { get_release_stage rel_stage "${current_modulefile}" "${UsedReleaseStages}" } - # + #...................................................................... # output load 'hints' # # Note: @@ -353,31 +434,33 @@ subcommand_load() { # # Args: # none - output_load_hints() { + get_load_hints() { + local "$1" local output='' local rel_stage='' while read -a line; do + [[ -z ${line} ]] && continue rel_stage=${line[1]} if [[ ! ":${UsedReleaseStages}:" =~ "${rel_stage}" ]]; then output+="module use ${rel_stage}; " fi local group=${line[2]} if [[ ! ":${UsedGroups}:" =~ ":${group}:" ]] && \ - (( ${GroupDepths[${group}]} == 0 )); then + (( ${GroupDepths[${group}]} == 0 )); then output+="module use ${group}; " fi - output+="module load ${line[@]:3} ${line[0]}\n" - done < <(subcommand_search "${m}" -a --no-header 2>&1) - if [[ -n "${output}" ]]; then - std::info "\nTry with one of the following command(s):" - std::die 3 "${output}\n" - fi + local -i n=$(( ${GroupDepths[${group}]}/2 )) + output+="module load ${line[@]:3:$n} ${line[0]}\n" + done < <(set +x; subcommand_search "${m}" -a --no-header 2>&1) + std::upvar $1 "${output}" } + #...................................................................... module_is_loaded() { [[ :${LOADEDMODULES}: =~ :$1: ]] } + #...................................................................... load_dependencies() { local -r fname="$1" while read dep; do @@ -388,8 +471,55 @@ subcommand_load() { done < "${fname}" } + #...................................................................... + # Check whether argument is a group. + # + # In the following function we test whether the group exist in + # an overlay. It doesn't matter which overlay. If we find an + # overlay providing modules for this group, we compute the + # hierarchical depth of the group and save this value for later + # use. + # + # Args: + # $1: group + # + # Notes: + # This function is used with extended module names. + # + # Multiple overlays may provide modules for the same group. But the + # hierarchical depth of a group must always be the same. + # + is_group () { + local "$1" + find_overlay_with_group() { + local "$1" + local -r group="$2/${PMODULES_MODULEFILES_DIR}" + local ol + for ol in "${UsedOverlays[@]}"; do + local inst_root="${OverlayInfo[${ol}:inst_root]}" + if [[ -d "${inst_root}/${group}" ]]; then + std::upvar $1 "${ol}" + return 0 + fi + done + return 1 + } + local -r group="$1" + # arg isn't emtpy and group already in cache + [[ -n ${group} ]] && [[ -n ${GroupDepths[${group}]} ]] && return 0 + local ol='' + find_overlay_with_group ol "${group}" || 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' + } + + #...................................................................... local args=() - opts=() + local opts=() + local overlay while (($# > 0)); do case $1 in -\? | -H | --help ) @@ -415,14 +545,20 @@ subcommand_load() { * ) args+=( $1 ) ;; - esac - shift + esac + shift done if (( ${#args[@]} == 0 )); then - std::die 2 "%s %s: %s\n" \ + std::die 2 "%s %s: %s" \ "${CMD}" "${subcommand}" \ "No module specified" fi + + IFS=':' + local -a modulepath=(${MODULEPATH}) + unset IFS + + local m='' for m in "${args[@]}"; do if [[ "$m" =~ ":" ]]; then @@ -463,52 +599,50 @@ subcommand_load() { fi if [[ -n ${group} ]]; then is_group "${group}" || \ - std::die 3 "%s %s: %s -- %s\n" \ + std::die 3 "%s %s: %s -- %s" \ "${CMD}" "${subcommand}" \ "illegal group name" \ "${group}" local -i depth=${GroupDepths[${group}]} (( depth != 0 )) && \ - std::die 3 "%s %s: %s -- %s\n" \ + std::die 3 "%s %s: %s -- %s" \ "${CMD}" "${subcommand}" \ "illegal group name" \ "${group}" - MODULEPATH="${PMODULES_ROOT}/${group}/" - MODULEPATH+="${PMODULES_MODULEFILES_DIR}" - modulepath=( ${MODULEPATH} ) + 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 is_release_stage "${rel_stage}" || \ - std::die 3 "%s %s: %s -- %s\n" \ + std::die 3 "%s %s: %s -- %s" \ "${CMD}" "${subcommand}" \ - "illegal release stage" - "${rel_stage}" + "illegal release stage" \ + "${rel_stage}" std::append_path UsedReleaseStages "${rel_stage}" g_env_must_be_saved='yes' fi - fi - local found='' - for flag in "${UsedFlags[@]/#/_}" ''; do - # :FIXME: this doesn't work if ${m} is a - # modulename without version - if is_available "${m}"; then - m+="${flag}" - found=':' - break - fi - done - if [[ ! "${found}" ]]; then - std::info "%s %s: module unavailable -- %s" \ - "${CMD}" 'load' "${m}" - [[ ${verbosity_lvl} == 'verbose' ]] && output_load_hints - std::die 3 "" - fi - if [[ ${current_modulefile} =~ ${PMODULES_ROOT} ]] \ - && [[ ! ${m} =~ / ]]; then - # the module is in our hierarchy but no version - # has been specified. We take the version from - # current_modulefile and append it - m+="/${current_modulefile##*/}" + fi # handle extended module names + find_module current_modulefile rel_stage "${m}" "${modulepath[@]}" + if [[ -z ${current_modulefile} ]]; then + local text='' + get_load_hints text + if [[ -z "${text}" ]]; then + std::die 3 "%s %s: module does not exist -- %s" \ + "${CMD}" 'load' "${m}" + else + std::info "%s %s: module unavailable -- %s" \ + "${CMD}" 'load' "${m}" + if [[ ${verbosity_lvl} == 'verbose' ]]; then + std::info '' + std::info "Try with one of the following command(s):" + std::info "${text}" + fi + std::die 3 "" + fi fi if [[ ${m} == Pmodules/* ]] && [[ -n ${LOADEDMODULES} ]]; then std::error "%s %s: %s" \ @@ -520,14 +654,17 @@ subcommand_load() { "${m}" fi if [[ ":${LOADEDMODULES}:" =~ ":${m}:" ]]; then + # already loaded continue fi - if [[ ${current_modulefile} =~ ${PMODULES_ROOT} ]]; then - # modulefile is in our hierarchy - # ${prefix} was set in is_available()! + + local prefix='' + get_module_prefix prefix "${current_modulefile}" + if [[ -n ${prefix} ]]; then test -r "${prefix}/.info" && cat "$_" 1>&2 test -r "${prefix}/.dependencies" && load_dependencies "$_" - fi + fi + local output=$("${modulecmd}" 'bash' ${opts} 'load' \ "${current_modulefile}" 2> "${tmpfile}") @@ -543,9 +680,9 @@ subcommand_load() { local s=${error%%$'\n'*} local error_txt='failed' if [[ "$s" =~ ' conflicts ' ]]; then - error_txt='conflicts with already loaded modules' + error_txt="conflicts with already loaded module(s): ${s#*module(s) }" fi - std::die 3 "%s %s: %s -- %s\n" \ + std::die 3 "%s %s: %s -- %s" \ "${CMD}" "${subcommand}" \ "${error_txt}" \ "${m}" @@ -577,9 +714,13 @@ 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}}" done <<< "${MODULEPATH//:/$'\n'}" + g_env_must_be_saved='yes' export_env 'LOADEDMODULES' } @@ -623,7 +764,7 @@ subcommand_unload() { shift done if (( ${#args[@]} == 0 )); then - std::die 3 "%s %s: %s\n" \ + std::die 3 "%s %s: %s" \ "${CMD}" "${subcommand}" \ "missing argument" fi @@ -639,14 +780,14 @@ subcommand_unload() { local arg for arg in "${args[@]}"; do local output=$("${modulecmd}" "${Shell}" 'unload' "${arg}") - eval "$(echo "${output}"|sed -e 's/;unalias [^;]*//g')" + eval "$(echo "${output}"|${sed} -e 's/;unalias [^;]*//g')" case ${Shell} in - sh | bash | zsh ) - echo "${output}" - ;; - * ) - "${modulecmd}" "${Shell}" 'unload' "${arg}" - ;; + sh | bash | zsh ) + echo "${output}" + ;; + * ) + "${modulecmd}" "${Shell}" 'unload' "${arg}" + ;; esac done if [[ -z ${PMODULES_HOME} ]]; then @@ -654,7 +795,7 @@ subcommand_unload() { export_env 'PMODULES_HOME' fi g_env_must_be_saved='yes' -} +} # subcommand_unload ############################################################################## # @@ -669,7 +810,7 @@ USAGE: module swap [modulefile1] modulefile2 Switch loaded modulefile1 with modulefile2. If modulefile1 is not specified, then it is assumed to be the currently - loaded module with the same root name as modulefile2. + loaded module with the same base name as modulefile2. " subcommand_swap() { @@ -692,11 +833,11 @@ subcommand_swap() { shift done if (( ${#args[@]} == 0 )); then - std::die 3 "%s %s: %s\n" \ + std::die 3 "%s %s: %s" \ "${CMD}" "${subcommand}" \ "missing argument" elif (( ${#args[@]} > 2 )); then - std::die 3 "%s %s: %s\n" \ + std::die 3 "%s %s: %s" \ "${CMD}" "${subcommand}" \ "too many arguments" fi @@ -749,7 +890,7 @@ subcommand_show() { shift done if (( ${#args[@]} == 0 )); then - std::die 3 "%s %s: %s\n" \ + std::die 3 "%s %s: %s" \ "${CMD}" "${subcommand}" \ "missing argument" fi @@ -760,39 +901,185 @@ subcommand_show() { done } +############################################################################### +# 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 # -# get all available modules in given directory. # return list like -# modulename_1 rel_stage_1 modulefile_1 modulename_2 rel_stage_2 modulefile_1 ... +# modulename_1 rel_stage_1 modulefile_1 ... # get_available_modules() { - local var="$1" + local var="$1" local -r module="$2" local -r used_rel_stages="${3:-${UsedReleaseStages}}" shift 3 # in the for loop below we use $@ to loop over the directories - local -a mods=() - local rel_stage='' - local dir='' - - for dir in "$@"; do - test -d "${dir}" || continue - { - cd "${dir}" - while read mod; do - get_release_stage rel_stage "${dir}/${mod}" - - if [[ :${used_rel_stages}: =~ :${rel_stage}: ]]; then - mods+=( "${mod}" ${rel_stage} "${dir}/${mod}") + local rel_stage + + local -A dict + local -A modulenames + local dir + for dir in "$@"; do + test -d "${dir}" || continue + { + cd "${dir}" + # there might be no mapping for ${dir}! + # - after loading the parent of a hierarchical group + # - if we do a search + # - if we create a new hierarchical group + local ol='' + local group='' + find_overlay ol group "${dir}" + + # if no modules are installed in ${dir}, '*' expands to + # the empty string! Using '*' in the find command below + # would cause problems with some non-GNU find + # implementations. + local entries=$(echo *) + [[ -n ${entries} ]] || continue + local mod='' # module_name/module_version + while read mod; do + local add='no' + if [[ -n "${ol}" ]]; then + # module is in an overlay + # + # add to list of available modules, if + # - first time found by name only + # - in same overlay as first found + # - new version and not hidden by overlay + local name="${mod%/*}" + local key="${dir##/${PMODULES_MODULEFILES_DIR}}/${name}" + if [[ -z "${modulenames[${key}]}" ]]; then + if [[ "${OverlayInfo[${ol}:type]}" == "${ol_hiding}" ]]; then + modulenames[${key}]="${ol}" + else + modulenames[${key}]='0' + fi + add='yes' + elif [[ "${modulenames[${key}]}" == "${ol}" ]]; then + add='yes' + elif [[ "${modulenames[${key}]}" == '0' ]] \ + && [[ -z ${dict[${mod}]} ]]; then + add='yes' + fi + else + # module is NOT in an overlay + add='yes' fi - done < <(${find} -L * \ - \( -type f -o -type l \) -not -name ".*" \ - -ipath "${module}" \ + [[ "${add}" == 'yes' ]] || continue + get_release_stage \ + rel_stage \ + "${dir}" \ + "${mod}" + [[ :${used_rel_stages}: =~ :${rel_stage}: ]] || continue + + mods+=( "${mod}" ${rel_stage} "${dir}/${mod}" "${ol}" ) + dict[${mod}]=1 + done < <(${find} -L ${entries} \ + \( -type f -o -type l \) \ + -not -name ".*" \ + -ipath "${module}*" \ | ${sort} --version-sort) - } - done + } + done std::upvar ${var} "${mods[@]}" -} +} # get_available_modules() + +# +# find module(file) to load. Input arguments are +# $1 upvar: return module file +# $2 upvar: return module release stage +# $3 module to load +# $4 a modulepath (usually MODULEPATH) +# +# The module name can be +# name +# name/version +# name/version_flag +# +# Return +# return code 0 and modulefile in first argument +# return code 1 and modulefile in first argument +# if module +# return code 2 +# if no modulefile could be found +# +find_module() { + local "$1" + local "$2" + local -r module="$3" + local -a dirs=("${@:4}") + + for dir in "${dirs[@]}"; do + test -d "${dir}" || continue + local -i col=$((${#dir} + 2 )) + local -a modules + if [[ ${module} == */* ]]; then + # a version number has been specified. But we still might + # have the same module/version with different use flags. + # The different release stages we ignore in this case. + modules=( $(${find} -L "${dir}" -type f -not -name ".*" \ + -ipath "${dir}/${module}*" \ + | cut -b${col}-) ) + for mod in "${modules[@]}"; do + # + # loop over all used flags. If a module with + # a used flag is available load this module. + local rel_stage + for flag in "${UseFlags[@]/#/_}" ""; do + [[ ${mod} == ${module}${flag} ]] || continue + std::upvar $1 "${dir}/${mod}" + get_release_stage \ + rel_stage \ + "${dir}" \ + "${mod}" + std::upvar $2 "${rel_stage}" + return 0 + done + done + else + # 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 + # a used flag is available load this module. + local rel_stage='' + for flag in "${UseFlags[@]/#/_}" ""; do + [[ ${mod} == ${module}/*${flag} ]] || continue + std::upvar $1 "${dir}/${mod}" + get_release_stage \ + rel_stage \ + "${dir}" \ + "${mod}" + std::upvar $2 "${rel_stage}" + [[ :${rel_stage}: =~ :${UsedReleaseStages}: ]] && \ + return 0 + done + done + fi + done + return 1 +} # find_module() ############################################################################## # @@ -842,6 +1129,7 @@ subcommand_avail() { # get number of columns of terminal cols=$(tput cols) + #...................................................................... output_header() { local caption="$1" let i=($cols-${#caption})/2-2 @@ -851,10 +1139,11 @@ subcommand_avail() { printf -- "\n" 1>&2 } + #...................................................................... 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 @@ -870,17 +1159,18 @@ subcommand_avail() { std::info "" } + #...................................................................... 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 } - # + #...................................................................... # :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 @@ -896,13 +1186,14 @@ subcommand_avail() { std::info "" } + #...................................................................... human_readable_output() { output_header "$1" 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 @@ -941,6 +1232,8 @@ subcommand_avail() { done printf -- "\n\n" 1>&2 } + + #...................................................................... local pattern=() local output_function='human_readable_output' local opt_use_rel_stages="${UsedReleaseStages}" @@ -992,28 +1285,37 @@ subcommand_avail() { IFS=':' local -a modulepath=(${MODULEPATH}) unset IFS - local string - for string in "${pattern[@]}"; do - for dir in "${modulepath[@]}"; do - if [[ ${dir} =~ ${PMODULES_ROOT} ]]; then - local group="${dir/${PMODULES_ROOT}\/}" - group="${group%%/*}" - else - local group="${dir}" - fi + local dir + local group='' + local ol='' + for dir in "${modulepath[@]}"; do + group='other' + find_overlay ol group "${dir}" + if [[ ! -v modulepath_${group} ]]; then + typeset -a modulepath_${group} + fi + typeset -n path=modulepath_${group} + path+=("${dir}") + done + local p + for string in "${pattern[@]}"; do + for group in ${UsedGroups//:/ } other; do if (( ${#opt_groups[@]} > 0 )) && [[ ! -v opt_groups[${group}] ]]; then continue fi + [[ -v modulepath_${group} ]] || continue + typeset -n path=modulepath_${group} get_available_modules \ mods \ "${string}*" \ "${opt_use_rel_stages}" \ - "${dir}" + "${path[@]}" + [[ ${#mods[@]} == 0 ]] && continue ${output_function} "${group}" done done -} +} # subcommand_avail() ############################################################################## # @@ -1052,23 +1354,52 @@ subcommand_use() { unset IFS local add2path_func='std::append_path' + #...................................................................... group_is_used() { [[ :${UsedGroups}: =~ :$1: ]] } + #...................................................................... print_info() { + print_ol_info(){ + local only_used="$1" + local ol='' + for ol in "${Overlays[@]}"; do + [[ ${OverlayInfo[${ol}:used]} == ${only_used} ]] || continue + local inst_root="${OverlayInfo[${ol}:inst_root]}" + local mod_root="${OverlayInfo[${ol}:mod_root]}" + local txt="\t${ol}" + if [[ ${inst_root} == ${mod_root} ]]; then + txt+="\n\t\t${inst_root}" + else + txt+="\n\t\t${inst_root} (install root)" + txt+="\n\t\t${mod_root} (modulefiles root)" + fi + case "${OverlayInfo[${ol}:type]}" in + "${ol_hiding}" ) + txt+='\n\t\t(hiding modules with same name)' + ;; + "${ol_replacing}" ) + txt+='\n\t\t(replacing groups)' + ;; + esac + std::info "${txt}" + done + } + local f local r std::info "Used groups:" for f in ${UsedGroups//:/ }; do std::info "\t${f}" done - std::info "\nUnused groups:" + std::info '' + std::info "Unused groups:" local _group for _group in "${!GroupDepths[@]}"; do local -i depth=${GroupDepths[${_group}]} if ! group_is_used "${_group}" && (( depth == 0 )); then - std::info "\t${_group}" + std::info "\t${_group}" fi done @@ -1086,10 +1417,20 @@ subcommand_use() { std::info "\t${flag}" done - std::info "\nAdditonal directories in MODULEPATH:" + std::info '' + std::info "Used overlays:" + print_ol_info 'yes' + + std::info '' + std::info "Unused overlays:" + print_ol_info 'no' + + std::info '' + std::info "Additonal directories in MODULEPATH:" let n=0 + local group for (( i=0; i<${#modulepath[@]}; i++)); do - if [[ ! ${modulepath[i]} =~ ${PMODULES_ROOT} ]]; then + if ! find_overlay ol group "${modulepath[i]}"; then std::info "\t${modulepath[i]}" let n+=1 fi @@ -1098,66 +1439,123 @@ subcommand_use() { std::info "\tnone" fi std::info "" - } + } # print_info + #...................................................................... use () { + use_overlay() { + local ol_name="$1" + + if [[ -n "${LOADEDMODULES}" ]] && \ + [[ "${LOADEDMODULES}" != Pmodules/+([.0-9rc]) ]]; then + std::die 3 "%s %s: %s %s" \ + "${CMD}" "${subcommand}" \ + "overlay cannot be added since some" \ + "modules are already loaded!" + fi + + if [[ ${OverlayInfo[${ol_name}:used]} == 'yes' ]]; then + std::die 3 "%s %s: %s -- %s" \ + "${CMD}" "${subcommand}" \ + "overlay already in use" \ + "${ol_name}" + return 0 + fi + + 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="${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 "${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="${OverlayInfo[${ol_name}:mod_root]}/" + dir+="${group}/${PMODULES_MODULEFILES_DIR}" + if [[ -d "${dir}" ]]; then + std::prepend_path MODULEPATH "${dir}" + fi + done + + UsedOverlays=( "${ol_name}" "${UsedOverlays[@]}" ) + OverlayInfo[${ol_name}:used]='yes' + export_env UsedOverlays + g_env_must_be_saved='yes' + scan_groups "${UsedOverlays[@]}" + } + + #.............................................................. + use_group() { + if (( ${GroupDepths[${arg}]} > 0 )); then + # argument is a hierarchical group in our root + std::die 3 "%s %s: %s -- %s" \ + "${CMD}" "${subcommand}" \ + "illegal group" \ + "${arg}" + fi + std::append_path UsedGroups "$1" + local ol_name + for ol_name in "${UsedOverlays[@]}"; do + local dir="${OverlayInfo[${ol_name}:mod_root]}/$1/${PMODULES_MODULEFILES_DIR}" + [[ -d "${dir}" ]] || continue + + std::prepend_path MODULEPATH "${dir}" + [[ "${OverlayInfo[${ol_name}:type]}" == "${ol_replacing}" ]] && break + done + } + + #.............................................................. local arg=$1 if is_release_stage "${arg}"; then # argument is release stage std::append_path UsedReleaseStages "${arg}" - return + return $? fi if [[ "${arg}" =~ "flag=" ]]; then # argument is flag UsedFlags+=( "${arg/flag=}" ) - return + return $? fi - if [[ -z ${GroupDepths[${arg}]} ]] && [[ -d "${PMODULES_ROOT}/${arg}" ]]; then - scan_groups "${PMODULES_ROOT}" - g_env_must_be_saved='yes' + if [[ -v OverlayInfo[${arg}:type] ]]; then + use_overlay "${arg}" + return $? fi - - if [[ -n ${GroupDepths[${arg}]} ]] && - (( ${GroupDepths[${arg}]} == 0 )); then - # argument is group in our root with depth 0 - std::append_path UsedGroups "${arg}" - local dir="${PMODULES_ROOT}/${arg}/" - dir+="${PMODULES_MODULEFILES_DIR}" - ${add2path_func} MODULEPATH "${dir}" - return + if [[ -d ${arg} ]]; then + local dir=$(cd "${arg}" && pwd -L) + ${add2path_func} MODULEPATH "${dir}" + return $? + fi + if [[ ! -v GroupDepths[${arg}] ]]; then + # this scan is required if a new group has been + # create inside an used overlay + scan_groups "${UsedOverlays[@]}" + g_env_must_be_saved='yes' fi - if [[ -n ${GroupDepths[${arg}]} ]] && - (( ${GroupDepths[${arg}]} > 0 )); then - # argument is a hierarchical group in our root - std::die 3 "%s %s: %s -- %s\n" \ - "${CMD}" "${subcommand}" \ - "illegal group" \ - "${arg}" - return - fi - # arg must be a directory! - if [[ ! -d ${arg} ]]; then - std::die 3 "%s %s: %s -- %s\n" \ - "${CMD}" "${subcommand}" \ - "illegal argument" \ - "${arg}" - return + if [[ -n ${GroupDepths[${arg}]} ]]; then + use_group "${arg}" + return $? fi - dir="$(cd "${arg}" && pwd)" - if [[ ${dir} =~ ^${PMODULES_ROOT} ]]; then - # argument is somehing in our root - std::die 3 "%s %s: %s -- %s\n" \ - "${CMD}" "${subcommand}" \ - "illegal argument" \ - "${arg}" - return - fi - # argument is a modulepath - ${add2path_func} MODULEPATH "$(cd "${arg}" && pwd)" - } + std::die 3 "%s %s: %s -- %s" \ + "${CMD}" "${subcommand}" \ + "invalid keyword, group, overlay or directory" \ + "${arg}" + } # use () + #...................................................................... local -a args=() while (( $# > 0)); do case "$1" in @@ -1207,7 +1605,94 @@ unuse directory|group|release... subcommand_unuse() { local -r subcommand='unuse' + IFS=':' + local -a modulepath=(${MODULEPATH}) + unset IFS + + #...................................................................... unuse() { + #.............................................................. + unuse_overlay() { + local ol_name="$1" + + if [[ -n "${LOADEDMODULES}" ]] && \ + [[ "${LOADEDMODULES}" != Pmodules/+([.0-9rc]) ]]; then + std::die 3 "%s %s: %s %s" \ + "${CMD}" "${subcommand}" \ + "overlay cannot be removed since" \ + "some modules are still loaded!" + fi + + [[ "${OverlayInfo[ol_name]:mod_root}" == "${PMODULES_HOME%%/Tools*}" ]] && \ + std::die 3 "%s %s: %s -- %s" \ + "${CMD}" "${subcommand}" \ + "cannot remove base overlay" \ + "${ol_name}" + [[ ${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) + 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 [[ "${OverlayInfo[${ol_name}:type]}" == "${ol_replacing}" ]]; then + # if this overlay hides groups, we have to re-add + # the modules made available by other overlays + local mod_root=${OverlayInfo[${ol_name}:mod_root]} + 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 "${UsedOverlays[@]/%/${dir}}" + done + fi + + OverlayInfo[${ol_name}:used]='no' + UsedOverlays=( "${UsedOverlays[@]:1}") + g_env_must_be_saved='yes' + export_env UsedOverlays + local dir + for dir in "${modulepath[@]}"; do + [[ "${dir}" =~ "${OverlayInfo[${ol_name}:mod_root]}" ]] && \ + std::remove_path MODULEPATH "${dir}" + done + } + + #.............................................................. + unuse_group() { + if (( ${GroupDepths[${arg}]} > 0 )); then + # argument is a hierarchical group in our root + std::die 3 "%s %s: %s -- %s" \ + "${CMD}" "${subcommand}" \ + "illegal group" \ + "${arg}" + fi + + local var="PMODULES_LOADED_${arg^^}" + if [[ -n "${!var}" ]]; then + std::die 3 "%s %s: %s -- %s" \ + "${CMD}" "${subcommand}" \ + "cannot remove group due to loaded modules" \ + "${arg}" + fi + std::remove_path UsedGroups "${arg}" + local overlay + for overlay in "${UsedOverlays[@]}"; do + local dir="${overlay}/${arg}/${PMODULES_MODULEFILES_DIR}" + std::remove_path MODULEPATH "${dir}" + done + } + + #.............................................................. local arg=$1 if is_release_stage "${arg}"; then @@ -1224,54 +1709,26 @@ subcommand_unuse() { done return fi - if [[ -n ${GroupDepths[${arg}]} ]] && - (( ${GroupDepths[${arg}]} == 0 )); then - # argument is group in our root with depth 0 - local var="PMODULES_LOADED_${arg^^}" - if [[ -n "${!var}" ]]; then - std::die 3 "%s %s: %s -- %s\n" \ - "${CMD}" "${subcommand}" \ - "cannot remove group due to loaded modules" \ - "${arg}" - fi - std::remove_path UsedGroups "${arg}" - local dir="${PMODULES_ROOT}/${arg}/" - dir+="${PMODULES_MODULEFILES_DIR}" + if [[ -v OverlayInfo[${arg}:type] ]]; then + unuse_overlay "${arg}" + return 0 + fi + if [[ -d ${arg} ]]; then + local dir=$(cd "${arg} && pwd -L") std::remove_path MODULEPATH "${dir}" - return - fi - if [[ -n ${GroupDepths[${arg}]} ]] && - (( ${GroupDepths[${arg}]} > 0 )); then - # argument is a hierarchical group in our root - std::die 3 "%s %s: %s -- %s\n" \ - "${CMD}" "${subcommand}" \ - "illegal group" \ - "${arg}" - return - fi - # arg must be a directory! - if [[ ! -d ${arg} ]]; then - std::die 3 "%s %s: %s -- %s\n" \ - "${CMD}" "${subcommand}" \ - "illegal argument" \ - "${arg}" + fi + if [[ -n ${GroupDepths[${arg}]} ]]; then + unuse_group "${arg}" return fi - dir="$(cd "${arg}" && pwd)" - if [[ ${dir} =~ ^${PMODULES_ROOT} ]]; then - # argument is somehing in our root - std::die 3 "%s %s: %s -- %s\n" \ - "${CMD}" "${subcommand}" \ - "illegal argument" \ - "${arg}" - return - fi - # argument is a modulepath - std::remove_path MODULEPATH "${dir}" - - } + std::die 3 "%s %s: %s -- %s" \ + "${CMD}" "${subcommand}" \ + "invalid keyword, group, overlay or directory" \ + "${arg}" + } # unuse() + #...................................................................... local -a args=() while (( $# > 0)); do case "$1" in @@ -1290,7 +1747,7 @@ subcommand_unuse() { shift done if (( ${#args[@]} == 0 )); then - std::die 3 "%s %s: %s\n" \ + std::die 3 "%s %s: %s" \ "${CMD}" "${subcommand}" \ 'missing argument' fi @@ -1340,76 +1797,87 @@ subcommand_refresh() { subcommand_generic0 'refresh' "$@" } -reset_modulepath() { - MODULEPATH='' - local group - local root="${PMODULES_ROOT}" - for group in ${UsedGroups//:/ }; do - local dir="${root}/${group}/${PMODULES_MODULEFILES_DIR}" - [[ -d "${dir}" ]] && std::prepend_path MODULEPATH "${dir}" - done - g_env_must_be_saved='yes' -} - -reset_used_groups() { - UsedGroups='' - local group - for group in ${DefaultGroups}; do - std::append_path UsedGroups "${group}" - done - g_env_must_be_saved='yes' -} - -reset_used_releases() { - declare -g UsedReleaseStages='' - for r in ${DefaultReleaseStages//:/ }; do - std::append_path UsedReleaseStages "${r}" +# +# Helper functions, used during initialization and for purging all modules. +# +# Arguments: +# none +# +init_modulepath() { + declare -gx MODULEPATH='' + local group + local ol + for ol in "${UsedOverlays[@]}"; do + for group in ${UsedGroups//:/ }; do + local dir="${OverlayInfo[${ol}:mod_root]}/${group}/${PMODULES_MODULEFILES_DIR}" + if [[ -d "${dir}" ]]; then + std::prepend_path MODULEPATH "${dir}" + fi + done done - g_env_must_be_saved='yes' -} - -init_manpath() { - std::replace_path MANPATH "${PMODULES_HOME%/*}/.*" - - if [[ -r /etc/man.config ]]; then - declare _manconf='/etc/man.config' - elif [[ -r /etc/man.conf ]]; then - declare _manconf='/etc/man.conf' - fi - if [[ -n ${_manconf} ]]; then - while read name value rest; do - std::append_path MANPATH "${value}" - done < <(grep "^MANPATH\s" "${_manconf}") - unset _manconf - else - std::append_path MANPATH "${PMODULES_HOME}/share/man" - std::append_path MANPATH "/usr/share/man" - fi } pmodules_init() { - if [[ -r "${pmodules_config_file}" ]]; then - source "${pmodules_config_file}" || \ - std::die 3 "Oops: cannot parse config file -- %s\n" \ - "${pmodules_config_file}" - fi - declare -gx LOADEDMODULES='' - declare -gx _LMFILES_='' - declare -gx UsedGroups='' - declare -gx MODULEPATH='' - declare -Ag GroupDepths='()' - declare -ag UsedFlags=() - declare -g Version="${PMODULES_VERSION}" + init_used_groups() { + declare -gx UsedGroups='' + local group + for group in ${DefaultGroups//:/ }; do + std::append_path UsedGroups "${group}" + done + g_env_must_be_saved='yes' + } + init_used_releases() { + declare -g UsedReleaseStages='' + for r in ${DefaultReleaseStages//:/ }; do + std::append_path UsedReleaseStages "${r}" + done + g_env_must_be_saved='yes' + } + init_overlay_vars() { + declare -ag UsedOverlays=( 'base' ) + OverlayInfo['base:used']='yes' + } - reset_used_groups - reset_modulepath - reset_used_releases + init_manpath() { + std::replace_path MANPATH "${PMODULES_HOME%/*}/.*" + + if [[ -r /etc/man.config ]]; then + declare _manconf='/etc/man.config' + elif [[ -r /etc/man.conf ]]; then + declare _manconf='/etc/man.conf' + fi + if [[ -n ${_manconf} ]]; then + while read name value rest; do + std::append_path MANPATH "${value}" + done < <(grep "^MANPATH\s" "${_manconf}") + unset _manconf + else + std::append_path MANPATH "${PMODULES_HOME}/share/man" + std::append_path MANPATH "/usr/share/man" + fi + } + + pm::read_config + + declare -gx LOADEDMODULES='' + declare -gx _LMFILES_='' + declare -Ag GroupDepths=() + declare -ag UsedFlags=() + declare -g Version="${PMODULES_VERSION}" + + init_used_groups + init_used_releases + init_overlay_vars + init_modulepath init_manpath + scan_groups "${UsedOverlays[@]}" + save_env export_env \ - LOADEDMODULES \ - _LMFILES_ \ - MODULEPATH \ - MANPATH + LOADEDMODULES \ + _LMFILES_ \ + MODULEPATH \ + PATH \ + MANPATH } ############################################################################## @@ -1453,18 +1921,20 @@ subcommand_purge() { if (( ${#args[@]} > 0 )); then std::die 3 "%s %s: %s" \ "${CMD}" "${subcommand}" \ - "no arguments allowd" + "no arguments allowed" fi # 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 @@ -1483,8 +1953,8 @@ subcommand_purge() { "${error_txt}" fi if [[ "${Shell}" == "sh" ]]; then - # for sh-like shells just echo - echo "${output}" + # for sh-like shells just echo + eval "$(echo "${output}"|${sed} -e 's/;unalias [^;]*//g')" else # re-run with right shell "${modulecmd}" "${Shell}" 'purge' @@ -1494,19 +1964,20 @@ 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 - reset_modulepath + init_modulepath - export_env MODULEPATH PMODULES_HOME + export_env MODULEPATH } - ############################################################################## # # list [-hlt] @@ -1549,14 +2020,13 @@ subcommand_list() { shift done if (( ${#args[@]} > 0 )); then - std::die 3 "%s %s: %s\n" \ + std::die 3 "%s %s: %s" \ "${CMD}" "${subcommand}" \ - "no arguments allowd" + "no arguments allowed" fi "${modulecmd}" "${Shell}" list "${opts[@]}" } - ############################################################################## # # clear @@ -1590,12 +2060,11 @@ subcommand_clear() { shift done if (( ${#args[@]} > 0 )); then - std::die 3 "%s %s: %s\n" \ + std::die 3 "%s %s: %s" \ "${CMD}" "${subcommand}" \ "no arguments allowed" fi pmodules_init - export_env LOADEDMODULES MODULEPATH _LMFILES_ } ############################################################################## @@ -1603,14 +2072,17 @@ subcommand_clear() { # search [switches] [STRING...] # Subcommands[search]='search' +Subcommands[find]='search' Options[search]='-o a\?H -l help -l no-header -l print-modulefiles ' Options[search]+='-l release-stage: -l with: -l all-release-stages -l src: -l print-csv ' Options[search]+='-l verbose ' Options[search]+='-l all-deps -l wrap ' -Options[search]+='-l glob' +Options[search]+='-l glob ' +Options[search]+='-l newest ' +Options[search]+='-l group:' Help[search]=' USAGE: - module search [switches] STRING... + module find|search [switches] STRING... Search installed modules. If an argument is given, search for modules whose name match the argument. @@ -1650,6 +2122,7 @@ SWITCHES: subcommand_search() { local -r subcommand='search' local modules=() + local groups=() local with_modules='//' local -ir cols=$(tput cols) # get number of columns of terminal local -i max_len_modulename=0 @@ -1662,6 +2135,7 @@ subcommand_search() { local opt_all_deps='no' local opt_wrap='no' local opt_glob='no' + local opt_newest='no' #..................................................................... # @@ -1680,7 +2154,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 @@ -1691,7 +2165,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}) } @@ -1704,12 +2178,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]}" @@ -1723,21 +2197,24 @@ 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}) + : } 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 @@ -1752,7 +2229,9 @@ subcommand_search() { } print_line_modulefile() { - std::info "$1 $4" + if (( $# >= 4 )) && [[ -n $4 ]]; then + std::info "$1 $4" + fi } print_line_csv() { @@ -1775,21 +2254,17 @@ subcommand_search() { print_default fi + local _script='' + if [[ ${opt_newest} == 'yes' ]]; then + _script='{} END{print}' + fi ${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}" | \ - ${awk} "${with_modules}") + done < <("${sort}" --version-sort -k 1,1 -k 6,6 -k 7,7 "${tmpfile}" | \ + ${awk} "${with_modules} ${_script}") } - get_module_prefix() { - local "$1" - local modulefile="$2" - local -r _prefix=$("${modulecmd}" bash show "${modulefile}" 2>&1 | \ - ${awk} '/_PREFIX |_HOME / {print $3; exit}') - std::upvar $1 "${_prefix}" - } - #..................................................................... # # search modules @@ -1800,70 +2275,65 @@ subcommand_search() { # :FIXME: # search () { - if [[ ${opt_glob} == 'yes' ]]; then - local -r module="$1" - else - local -r module="${1}*" - fi - + local module="$1" + local group="$2" # write results to a temporary file for later processing - local group - # loop over all groups - for group in "${!GroupDepths[@]}"; do - # loop over all directories which can be added to - # MODULEPATH inside current group - local depth=${GroupDepths[${group}]} - local s='' - if (( depth > 0 )); then - s=$(printf '/*%.0s' $(seq 1 ${depth})) - fi - local modulepath=( ${src_prefix[@]/%//${group}/modulefiles$s} ) + + # loop over all directories which can be added to + # MODULEPATH inside current group + local depth=${GroupDepths[${group}]} + local s='' + if (( depth > 0 )); then + s=$(printf '/*%.0s' $(seq 1 ${depth})) + fi + local modulepath=( ${src_prefix[@]/%//${group}/modulefiles$s} ) + + # get and print all available modules in $mpath + # with respect to the requested release stage + # tmpfile: module/version rel_stage group dependencies... + local mods + get_available_modules \ + mods \ + "${module}" \ + "${opt_use_rel_stages}" \ + "${modulepath[@]}" + local i=0 + 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]} - # get and print all available modules in $mpath - # with respect to the requested release stage - # tmpfile: module/version rel_stage group dependencies... - local mods - get_available_modules \ - mods \ - "${module}" \ - "${opt_use_rel_stages}" \ - "${modulepath[@]}" \ - - for (( i=0; i<${#mods[@]}; i+=3 )); do - local name=${mods[i]} - local rel_stage=${mods[i+1]} - local modulefile=${mods[i+2]} - - if (( ${#name} > max_len_modulename)); then - max_len_modulename=${#name} - fi - - if [[ "${opt_print_verbose}" == 'yes' ]] || [[ "${opt_all_deps}" == 'yes' ]]; then - local prefix='' - get_module_prefix prefix "${modulefile}" - local dependencies_file="${prefix}/.dependencies" - if [[ -n ${prefix} ]] && [[ -r "${dependencies_file}" ]]; then - deps=($(< "${dependencies_file}")) - else - deps=() - fi + if (( ${#name} > max_len_modulename)); then + max_len_modulename=${#name} + fi + + if [[ "${opt_print_verbose}" == 'yes' ]] || \ + [[ "${opt_all_deps}" == 'yes' ]]; then + local prefix='' + get_module_prefix prefix "${modulefile}" + local dependencies_file="${prefix}/.dependencies" + if [[ -n ${prefix} ]] && [[ -r "${dependencies_file}" ]]; then + deps=($(< "${dependencies_file}")) else - # get dependencies encoded in directory name - local deps=() - local -i j - IFS='/' - local toks=( ${modulefile} ) - for ((j = -depth-2; j < -2; j += 2)); do - deps+=( "${toks[*]: $j:2}" ); - done - unset IFS + deps=() fi - - echo ${name} ${rel_stage} ${group} ${modulefile} \ - ${deps[@]} >> "${tmpfile}" - done + else + # get dependencies encoded in directory name + local deps=() + local -i j + IFS='/' # note: IFS is used to concat in the for loop! + local toks=( ${modulefile} ) + for ((j = -depth-2; j < -2; j += 2)); do + deps+=( "${toks[*]: $j:2}" ); + done + unset IFS + fi + + echo ${name} ${rel_stage} ${group} ${modulefile} \ + ${ol} \ + ${deps[@]} >> "${tmpfile}" done - print_result "${tmpfile}" } while (( $# > 0 )); do @@ -1923,24 +2393,24 @@ subcommand_search() { ;; --src | --src=*) if [[ "$1" == --src ]]; then - local src_prefix="$2" + local dir="$2" shift else - local src_prefix="${1/--src=}" + local dir="${1/--src=}" fi - if [[ ! -e "${src_prefix}" ]]; then + if [[ ! -e "${dir}" ]]; then std::die 1 "%s %s: %s -- %s" \ "${CMD}" 'search' \ "illegal value for --src option" \ - "${src_prefix} does not exist" + "${dir} does not exist" fi - if [[ ! -d "${src_prefix}" ]]; then + if [[ ! -d "${dir}" ]]; then std::die 1 "%s %s: %s -- %s" \ "${CMD}" 'search' \ "illegal value for --src option" \ - "${src_prefix} is not a directory" + "${dir} is not a directory" fi - src_prefix=$(std::get_abspath "${src_prefix}") + src_prefix+=( $(std::get_abspath "${src_prefix}") ) ;; -v | --verbose ) opt_print_verbose='yes' @@ -1951,6 +2421,18 @@ subcommand_search() { --glob ) opt_glob='yes' ;; + --newest ) + opt_newest='yes' + ;; + --group | --group=* ) + if [[ $1 == *=* ]]; then + groups+=( ${1/--*=} ) + else + groups+=( $2 ) + shift + fi + ;; + -- ) shift 1 modules+=( "$@" ) @@ -1963,7 +2445,10 @@ subcommand_search() { shift done if [[ -z "${src_prefix}" ]]; then - src_prefix="${PMODULES_ROOT}" + local ol='' + for ol in "${UsedOverlays[@]}"; do + src_prefix+=( "${OverlayInfo[${ol}:mod_root]}" ) + done fi if [[ "${opt_use_rel_stages}" == ":" ]]; then @@ -1976,14 +2461,25 @@ subcommand_search() { # :FIXME: do we need this? if (( ${#GroupDepths[@]} == 0 )) || \ - [[ ${src_prefix} != ${PMODULES_ROOT} ]]; then + [[ ${src_prefix} != ${PMODULES_HOME%%/Tools*} ]]; then scan_groups "${src_prefix}" g_env_must_be_saved='yes' fi local module + if (( ${#groups[@]} == 0 )); then + groups=( "${!GroupDepths[@]}" ) + fi for module in "${modules[@]}"; do - search "${module}" + if [[ ${opt_glob} != 'yes' ]]; then + module+="*" + fi + local group + for group in "${groups[@]}"; do + search "${module}" "${group}" + done + print_result + echo -n > ${tmpfile} done } @@ -2100,19 +2596,24 @@ subcommand_whatis() { shift done - local mod_name='' - local file_name='' - while read mod_name file_name; do - [[ -n ${file_name} ]] || continue - local whatis=$("${modulecmd}" bash \ - whatis \ - "${file_name}" \ - 2>&1 1>/dev/null) - printf "%-25s: %s\n" "${mod_name}" "${whatis/*:}" 1>&2 - done < <(set +x; subcommand_search \ - --print-modulefiles \ - "${options[@]}" \ - "${args[@]}" 2>&1) + local group='' + for group in "${!GroupDepths[@]}"; do + local mod_name='' + local file_name='' + while read mod_name file_name; do + [[ -n ${file_name} ]] || continue + local whatis=$("${modulecmd}" bash \ + whatis \ + "${file_name}" \ + 2>&1 1>/dev/null) + printf "%-25s: %s\n" "${mod_name}" "${whatis/*:}" 1>&2 + done < <(set +x; subcommand_search \ + --group "${group}" \ + --print-modulefiles \ + --newest \ + "${options[@]}" \ + "${args[@]}" 2>&1) + done } ############################################################################## @@ -2162,21 +2663,24 @@ subcommand_apropos() { "more then one search string specified" fi local arg="${args[0]}" - local mod_name='' - local file_name='' - while read mod_name file_name; do - [[ -n ${file_name} ]] || continue - local whatis=$("${modulecmd}" bash \ - whatis \ - "${file_name}" \ - 2>&1 1>/dev/null) - whatis="${whatis/*:}" - if [[ ${whatis,,} =~ ${arg,,} ]]; then - printf "%-25s: %s\n" "${mod_name}" "${whatis/*:}" 1>&2 - fi - done < <(set +x; subcommand_search \ + local group='' + for group in "${!GroupDepths[@]}"; do + local mod_name='' + local file_name='' + while read mod_name file_name; do + [[ -n ${file_name} ]] || continue + local whatis=$("${modulecmd}" bash \ + whatis \ + "${file_name}" \ + 2>&1 1>/dev/null) + if [[ ${whatis,,} =~ ${arg,,} ]]; then + printf "%-25s: %s\n" "${mod_name}" "${whatis/*:}" 1>&2 + fi + done < <(set +x; subcommand_search \ + --group "${group}" \ --print-modulefiles \ "${options[@]}" 2>&1) + done } ############################################################################## @@ -2281,7 +2785,7 @@ subcommand_initswitch() { shift done if (( ${#args[@]} != 2 )); then - std::die 3 "%s %s: %s\n" \ + std::die 3 "%s %s: %s" \ "${CMD}" "${subcommand}" \ "two arguments required not less not more" fi @@ -2326,6 +2830,11 @@ subcommand_initclear() { # # main # + +# parse arguments +# + +# first argument must be a shell! case "$1" in sh | bash | zsh ) declare Shell="sh" @@ -2334,11 +2843,12 @@ case "$1" in declare Shell='csh' ;; * ) - std::die 1 "${CMD}: unsupported shell -- $1\n" + std::die 1 "${CMD}: unsupported shell -- $1" ;; esac shift +# parse agruments till and including the sub-command declare -a opts=() while (( $# > 0 )); do case $1 in @@ -2366,72 +2876,44 @@ while (( $# > 0 )); do done if [[ -z "${subcommand}" ]]; then - std::die 1 "${CMD}: no sub-command specified.\n" + std::die 1 "${CMD}: no sub-command specified." fi if [[ -z "${Subcommands[${subcommand}]}" ]]; then - std::die 1 "${CMD}: unknown sub-command -- ${subcommand}\n" + std::die 1 "${CMD}: unknown sub-command -- ${subcommand}" fi -case ${subcommand} in - add ) - subcommand='load' - ;; - display ) - subcommand='show' - ;; - keyword ) - subcommand='apropos' - ;; - rm ) - subcommand='unload' - ;; - switch ) - subcommand='swap' - ;; -esac - +# restore variables from last call +unset Version 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}" - # 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 +fi +# Version should now be defined again, if not: +# - PMODULES_ENV was not set +# - Version was not defined the last time the status was saved. +# This is true for older Pmodules versions. + +# We (re-)initialise the Pmodules system, if +# - PMODULES_ENV was not set/is empty +# - A new Pmodules version has been loaded +if [[ -z ${Version} ]] || [[ ${Version} != ${PMODULES_VERSION} ]]; then + declare _tmp_loaded_modules_="${LOADEDMODULES}" + declare _tmp_lmfiles_="${_LMFILES_}" + pmodules_init + + LOADEDMODULES="${_tmp_loaded_modules_}" + _LMFILES_="${_tmp_lmfiles_}" + export_env \ + LOADEDMODULES \ + _LMFILES_ fi -if (( ${#GroupDepths[@]} == 0 )); then - scan_groups "${PMODULES_ROOT}" - g_env_must_be_saved='yes' -fi +# we need to handle help text and options for sub-cmd aliases +subcommand=${Subcommands[${subcommand}]} +# We need a tmp-file in the following sub-commands. It will be removed +# in the exit function if exists. case ${subcommand} in load|purge|search|swap|whatis|apropos ) declare -r tmpfile=$( ${mktemp} /tmp/Pmodules.XXXXXX ) \ @@ -2442,12 +2924,10 @@ case ${subcommand} in ;; esac - -tmp=$("${getopt}" --name="${CMD}" ${Options[${subcommand}]} -- "${opts[@]}" "$@" ) \ - || print_help "${subcommand}" -eval args=( "$tmp" ) -unset tmp -subcommand_${Subcommands[$subcommand]} "${args[@]}" +# parse arguments of the sub-command and call it +set -- $("${getopt}" --unquoted --name="${CMD}" ${Options[${subcommand}]} -- "${opts[@]}" "$@" ) \ + || print_help "${subcommand}" +subcommand_${Subcommands[$subcommand]} $@ # Local Variables: # mode: sh diff --git a/Pmodules/modulecmd.tcl.in.old b/Pmodules/modulecmd.tcl.in.old deleted file mode 100644 index 71a3d0f..0000000 --- a/Pmodules/modulecmd.tcl.in.old +++ /dev/null @@ -1,3489 +0,0 @@ -#!@PMODULES_HOME@/sbin/tclsh - -######################################################################## -# This is a pure TCL implementation of the module command -# to initialize the module environment, either -# - one of the scripts from the init directory should be sourced, or just -# - eval `/some-path/tclsh modulecmd.tcl MYSHELL autoinit` -# in both cases the path to tclsh is remembered and used furtheron -######################################################################## -# -# Some Global Variables..... -# - -regsub {\$[^:]+:\s*(\S+)\s*\$} {$Revision: 1.147 $} {\1}\ - MODULES_CURRENT_VERSION -set g_debug 0 ;# Set to 1 to enable debugging -set error_count 0 ;# Start with 0 errors -set g_autoInit 0 -set g_force 1 ;# Path element reference counting if == 0 -set CSH_LIMIT 4000 ;# Workaround for commandline limits in csh -set flag_default_dir 1 ;# Report default directories -set flag_default_mf 1 ;# Report default modulefiles and version alias - - -set PREFIX "" -set name "" -set version "" -set group "" -set g_url "" -set g_license "" -set g_maintainer "" -set g_help "" -set g_whatis "" - -# Used to tell if a machine is running Windows or not -proc isWin {} { - global tcl_platform - - if { $tcl_platform(platform) == "windows" } { - return 1 - } else { - return 0 - } -} - -# -# Set Default Path separator -# -if { [isWin] } { - set g_def_separator "\;" -} else { - set g_def_separator ":" -} - -# Dynamic columns -set DEF_COLUMNS 80 ;# Default size of columns for formatting -if {[catch {exec stty size} stty_size] == 0 && $stty_size != ""} { - set DEF_COLUMNS [lindex $stty_size 1] -} - -# Change this to your support email address... -set contact "root@localhost" - -# Set some directories to ignore when looking for modules. -set ignoreDir(CVS) 1 -set ignoreDir(RCS) 1 -set ignoreDir(SCCS) 1 -set ignoreDir(.svn) 1 -set ignoreDir(.git) 1 - -global g_shellType -global g_shell -set show_oneperline 0 ;# Gets set if you do module list/avail -t -set show_modtimes 0 ;# Gets set if you do module list/avail -l - -######################################################################## -# -# P m o d u l e s procedures -# - -# -# :TODO: -# switch/swap -# unload modules if parent removed -# - -if {[info exists env(PMODULES_DEBUG)] && $env(PMODULES_DEBUG)} { - proc debug {msg} { - set level [expr [info level] -2] - set r [catch {info level ${level}} e] - if {$r} { - set caller "" - } else { - set caller [lindex [split [info level [expr [info level] - 3]]] 0] - } - puts -nonewline stderr "${caller}: " - puts stderr ${msg} - } -} else { - proc debug {msg} {} -} - -proc procExists p { - return uplevel 1 [expr {[llength [info procs $p]] > 0}] -} - -proc module-addgroup { group } { - global env - global name - global version - - debug "called with arg $group, mode is \"[module-info mode]\"" - set Implementation [file join {*}$::implementation] - - set GROUP [string toupper $group] - regsub -- "-" ${GROUP} "_" GROUP - setenv ${GROUP} $name - setenv ${GROUP}_VERSION $version - - set ::${group} $name - set ::${group}_version $version - - if { [module-info mode load] } { - debug "mode is load" - - set dir $::PmodulesRoot/$group/$::PmodulesModulfilesDir/$Implementation - append-path MODULEPATH $dir - append-path PMODULES_USED_GROUPS $group - debug "mode=load: new MODULEPATH=$env(MODULEPATH)" - debug "mode=load: new PMODULES_USED_GROUPS=$env(PMODULES_USED_GROUPS)" - } elseif { [module-info mode unload] } { - set GROUP [string toupper $group] - debug "remove hierarchical group '${GROUP}'" - - if { [info exists env(PMODULES_LOADED_${GROUP})] } { - debug "unloading orphan modules" - set modules [split $env(PMODULES_LOADED_${GROUP}) ":"] - foreach m ${modules} { - if { ${m} == "999999999" } { - continue - } - if { [is-loaded ${m}] } { - debug "unloading: $m" - module unload ${m} - } - } - } else { - debug "no orphan modules to unload" - } - debug "mode=remove: $env(MODULEPATH)" - set dir $::PmodulesRoot/$group/$::PmodulesModulfilesDir/$Implementation - remove-path MODULEPATH $dir - remove-path PMODULES_USED_GROUPS $group - } - if { [module-info mode switch2] } { - debug "mode=switch2" - set dir $::PmodulesRoot/$group/$::PmodulesModulfilesDir/[module-info name] - append-path MODULEPATH $dir - append-path PMODULES_USED_GROUPS ${group} - } -} - -proc set-family { group } { - module-addgroup $group -} - -proc _pmodules_update_loaded_modules { group name version } { - if { ${group} == "999999999" } { - return - } - set GROUP [string toupper $group] - debug "${GROUP} $name/$version" - append-path PMODULES_LOADED_${GROUP} "$name/$version" - remove-path PMODULES_LOADED_${GROUP} "999999999" -} - -# -# load dependencies, but do *not* unload dependencies -# -proc _pmodules_load_dependencies { fname } { - if { ! [ file exists ${fname} ] } { - return - } - if { ! [module-info mode load] } { - return - } - debug "load dependencies from: ${fname}" - # Slurp up the data file - set fp [open ${fname} r] - set file_data [read ${fp}] - close ${fp} - set data [split ${file_data} "\n"] - foreach line ${data} { - debug "MODULEPATH=$::env(MODULEPATH)" - set module_name [string trim $line] - if { ${module_name} == "#" || ${module_name} == "" } { - continue - } - if { [is-loaded ${module_name}] } { - debug "module already loaded: ${module_name}" - continue - } - debug "module load: ${module_name}" - module load ${module_name} - } -} - -proc lreverse_n { list n } { - set res {} - set i [expr [llength $list] - $n] - while {$i >= 0} { - lappend res {*}[lrange $list $i [expr $i+$n-1]] - incr i -$n - } - set res -} - -# -# set standard environment variables -# -proc _pmodules_setenv { PREFIX name version } { - # - # Hack for supporting legacy modules - if { "${::group}" == "Legacy" } { - debug "this is a legacy module..." - return - } - - set NAME [string toupper $name] - regsub -- "-" ${NAME} "_" NAME - - if { ! [info exist ::dont-setenv] } { - set ::dont-setenv {} - } - - if { ${version} != "" } { - if { [lsearch ${::dont-setenv} "${NAME}_VERSION"] == -1 } { - setenv ${NAME}_VERSION $version - } - } - - if { [file isdirectory "$PREFIX"] } { - if { [lsearch ${::dont-setenv} "${NAME}_PREFIX"] == -1 } { - setenv ${NAME}_PREFIX $PREFIX - } - if { [lsearch ${::dont-setenv} "${NAME}_DIR"] == -1 } { - setenv ${NAME}_DIR $PREFIX - } - if { [lsearch ${::dont-setenv} "${NAME}_HOME"] == -1 } { - setenv ${NAME}_HOME $PREFIX - } - } else { - debug "$PREFIX is not a directory" - } - - if { [file isdirectory "$PREFIX/bin"] } { - if { [lsearch ${::dont-setenv} "PATH"] == -1 } { - prepend-path PATH $PREFIX/bin - } - } - - if { [file isdirectory "$PREFIX/sbin"] } { - if { [lsearch ${::dont-setenv} "PATH"] == -1 } { - prepend-path PATH $PREFIX/sbin - } - } - - if { [file isdirectory "$PREFIX/share/man"] } { - if { [lsearch ${::dont-setenv} "MANPATH"] == -1 } { - prepend-path MANPATH $PREFIX/share/man - } - } - - # set various environment variables - as long as they are not blacklisted - debug "prepend to include paths" - if { [file isdirectory "$PREFIX/include"] } { - if { [lsearch ${::dont-setenv} "C_INCLUDE_PATH"] == -1 } { - prepend-path C_INCLUDE_PATH $PREFIX/include - } - if { [lsearch ${::dont-setenv} "CPLUS_INCLUDE_PATH"] == -1 } { - prepend-path CPLUS_INCLUDE_PATH $PREFIX/include - } - if { [lsearch ${::dont-setenv} "${NAME}_INCLUDE_DIR"] == -1 } { - setenv ${NAME}_INCLUDE_DIR $PREFIX/include - } - } - - debug "prepend to library paths" - if { [file isdirectory "$PREFIX/lib"] } { - if { [lsearch ${::dont-setenv} "LIBRARY_PATH"] == -1 } { - prepend-path LIBRARY_PATH $PREFIX/lib - } - if { [lsearch ${::dont-setenv} "LD_LIBRARY_PATH"] == -1 } { - prepend-path LD_LIBRARY_PATH $PREFIX/lib - } - if { [lsearch ${::dont-setenv} "${NAME}_LIBRARY_DIR"] == -1 } { - setenv ${NAME}_LIBRARY_DIR $PREFIX/lib - } - } - - debug "prepend to library paths (64bit)" - if { [file isdirectory "$PREFIX/lib64"] } { - if { [lsearch ${::dont-setenv} "LIBRARY_PATH"] == -1 } { - prepend-path LIBRARY_PATH $PREFIX/lib64 - } - if { [lsearch ${::dont-setenv} "LD_LIBRARY_PATH"] == -1 } { - prepend-path LD_LIBRARY_PATH $PREFIX/lib64 - } - if { [lsearch ${::dont-setenv} "${NAME}_LIBRARY_DIR"] == -1 } { - setenv ${NAME}_LIBRARY_DIR $PREFIX/lib64 - } - } -} - -# -# intialize global vars -# Modulefile is something like -# -# ${PMODULES_ROOT}/group/${PMODULES_MODULEFILES_DIR}/name/version -# or -# ${PMODULES_ROOT}/group/${PMODULES_MODULEFILES_DIR}/X1/Y1/name/version -# or -# ${PMODULES_ROOT}/group/${PMODULES_MODULEFILES_DIR}/X1/Y1//X2/Y2/name/version -# -proc _init_global_vars { } { - global group - global name - global version - global implementation - global PREFIX # prefix of package - - debug "$::ModulesCurrentModulefile" - set ::PmodulesRoot $::env(PMODULES_ROOT) - set ::PmodulesModulfilesDir $::env(PMODULES_MODULEFILES_DIR) - set modulefile [file split $::ModulesCurrentModulefile] - set pmodules_root [file split $::PmodulesRoot] - set pmodules_root_num_dirs [llength $pmodules_root] - - set modulefile_root [file join {*}[lrange $modulefile 0 [expr $pmodules_root_num_dirs - 1]]] - if { $::PmodulesRoot != $modulefile_root } { - debug "stop sourcing: ${::PmodulesRoot} != $modulefile_root" - return - } - debug "modulefile is inside our root" - set rel_modulefile [lrange $modulefile [llength $pmodules_root] end] - set group [lindex $rel_modulefile 0] - set name [lindex $modulefile end-1] - set version [lindex $modulefile end] - set implementation [lrange $rel_modulefile 2 end] - set prefix "$pmodules_root $group [lreverse_n $implementation 2]" - set PREFIX [file join {*}$prefix] - - debug "PREFIX=$PREFIX" - debug "group of module $name: $group" -} - -proc _pmodules_output_message { fname } { - if { [ file exists "${fname}" ] } { - set fp [open "${fname}" r] - set info_text [read $fp] - close $fp - puts stderr ${info_text} - } -} - -if { [info exists ::whatis] } { - module-whatis "$whatis" -} - - -# -# we cannot load another module with the same name -# -#conflict $name - -#if { [module-info mode load] } { -# debug "${name}/${version}: loading ... " -# _pmodules_output_message "${PREFIX}/.info" -#} - - - -######################################################################## -# -# Info, Warnings and Error message handling. -# -proc reportWarning {message {nonewline ""}} { - if {$nonewline != ""} { - puts -nonewline stderr "$message" - } else { - puts stderr "$message" - } -} - -proc reportInternalBug {message} { - global contact - - puts stderr "Module ERROR: $message\nPlease contact: $contact" -} - -proc report {message {nonewline ""}} { - if {$nonewline != ""} { - puts -nonewline stderr "$message" - } else { - puts stderr "$message" - } -} - -######################################################################## -# Use a slave TCL interpreter to execute modulefiles -# - -proc unset-env {var} { - global env g_debug - - if {[info exists env($var)]} { - if {$g_debug} { - report "DEBUG unset-env: $var" - } - unset env($var) - } -} - -proc execute-modulefile {modfile {help ""}} { - global g_debug - global ModulesCurrentModulefile - global PREFIX - global name - global version - global group - global whatis - global g_url - global g_license - global g_maintainer - global g_help - - set ModulesCurrentModulefile $modfile - - if {$g_debug} { - report "DEBUG execute-modulefile: Starting $modfile" - } - _init_global_vars - set slave __[currentModuleName] - if {![interp exists $slave]} { - interp create $slave - interp alias $slave setenv {} setenv - interp alias $slave unsetenv {} unsetenv - interp alias $slave getenv {} getenv - interp alias $slave system {} system - interp alias $slave append-path {} append-path - interp alias $slave prepend-path {} prepend-path - interp alias $slave remove-path {} remove-path - interp alias $slave prereq {} prereq - interp alias $slave conflict {} conflict - interp alias $slave is-loaded {} is-loaded - interp alias $slave module {} module - interp alias $slave module-info {} module-info - interp alias $slave module-whatis {} module-whatis - interp alias $slave set-alias {} set-alias - interp alias $slave unset-alias {} unset-alias - interp alias $slave uname {} uname - interp alias $slave x-resource {} x-resource - interp alias $slave module-version {} module-version - interp alias $slave module-alias {} module-alias - interp alias $slave reportInternalBug {} reportInternalBug - interp alias $slave reportWarning {} reportWarning - interp alias $slave report {} report - interp alias $slave isWin {} isWin - - interp eval $slave {global ModulesCurrentModulefile g_debug} - interp eval $slave [list "set" "ModulesCurrentModulefile" $modfile] - interp eval $slave [list "set" "g_debug" $g_debug] - interp eval $slave [list "set" "help" $help] - - interp alias $slave _init_global_vars {} _init_global_vars - interp alias $slave _pmodules_setenv {} _pmodules_setenv - interp alias $slave _pmodules_update_loaded_modules {} _pmodules_update_loaded_modules - interp alias $slave debug {} debug - interp alias $slave module-url {} module-url - interp alias $slave module-license {} module-license - interp alias $slave module-maintainer {} module-maintainer - interp alias $slave module-help {} module-help - interp alias $slave module-addgroup {} module-addgroup - interp alias $slave set-family {} set-family - interp alias $slave output-help {} output-help - - interp eval $slave [list "set" "PREFIX" $PREFIX] - interp eval $slave [list "set" "name" $name] - interp eval $slave [list "set" "version" $version] - interp eval $slave [list "set" "group" $group] - } - set errorVal [interp eval $slave { - if {$g_debug} { - report "Sourcing $ModulesCurrentModulefile" - } - _init_global_vars - _pmodules_setenv ${::PREFIX} ${name} ${version} - _pmodules_update_loaded_modules ${group} ${name} ${version} - - proc ModulesHelp { } { - output-help - } - - - set sourceFailed [catch {source $ModulesCurrentModulefile} errorMsg] - if {$help != ""} { - if {[info procs "ModulesHelp"] == "ModulesHelp"} { - ModulesHelp - } else { - reportWarning "Unable to find ModulesHelp in $ModulesCurrentModulefile." - } - set sourceFailed 0 - } - if {$sourceFailed} { - if {$errorMsg == "" && $errorInfo == ""} { - unset errorMsg - return 1 - } elseif [regexp "^WARNING" $errorMsg] { - reportWarning $errorMsg - return 1 - } else { - global errorInfo - reportInternalBug "ERROR occurred in file\ - $ModulesCurrentModulefile:$errorInfo" - exit 1 - } - } else { - unset errorMsg - return 0 - } - }] - interp delete $slave - if {$g_debug} { - report "DEBUG Exiting $modfile" - } - return $errorVal -} - -proc module-url { url } { - set ::g_url ${url} -} - -proc module-license { license } { - set ::g_license ${license} -} - -proc module-maintainer { maintainer } { - set ::g_maintainer ${maintainer} -} - -proc module-help { help } { - set ::g_help ${help} -} - -proc output-help { } { - if { [info exists ::g_whatis] } { - report "${::g_whatis}" - } - if { [info exists ::version] } { - report "Version: ${::version}" - } - if { [info exists ::g_url] } { - report "Homepage: ${::g_url}" - } - if { [info exists ::g_license] } { - report "License: ${::g_license}" - } - if { [info exists ::g_maintainer] } { - report "Maintainer: ${::g_maintainer}" - } - if { [info exists ::g_help] } { - report "${::g_help}\n" - } -} - -# Smaller subset than main module load... This function runs modulerc and -# .version files -proc execute-modulerc {modfile} { - global g_rcfilesSourced - global g_debug g_moduleDefault - global ModulesCurrentModulefile - - - if {$g_debug} { - report "DEBUG execute-modulerc: $modfile" - } - - set ModulesCurrentModulefile $modfile - - if {![checkValidModule $modfile]} { - reportInternalBug "+(0):ERROR:0: Magic cookie '#%Module' missing in '$modfile'" - return "" - } - - set modparent [file dirname $modfile] - - if {![info exists g_rcfilesSourced($modfile)]} { - if {$g_debug} { - report "DEBUG execute-modulerc: sourcing rc $modfile" - } - set slave __.modulerc - if {![interp exists $slave]} { - interp create $slave - interp alias $slave uname {} uname - interp alias $slave system {} system - interp alias $slave module-version {} module-version - interp alias $slave module-alias {} module-alias - interp alias $slave module {} module - interp alias $slave reportInternalBug {} reportInternalBug - - interp eval $slave {global ModulesCurrentModulefile g_debug} - interp eval $slave [list "global" "ModulesVersion"] - interp eval $slave [list "set" "ModulesCurrentModulefile" $modfile] - interp eval $slave [list "set" "g_debug" $g_debug] - interp eval $slave {set ModulesVersion {}} - } - set ModulesVersion [interp eval $slave { - if [catch {source $ModulesCurrentModulefile} errorMsg] { - global errorInfo - reportInternalBug "occurred in file $ModulesCurrentModulefile:$errorInfo" - exit 1 - } elseif [info exists ModulesVersion] { - return $ModulesVersion - } else { - return {} - } - }] - interp delete $slave - - if {[file tail $modfile] == ".version"} { - # only set g_moduleDefault if .version file, - # otherwise any modulerc settings ala "module-version /xxx default" - # would get overwritten - set g_moduleDefault($modparent) $ModulesVersion - } - - if {$g_debug} { - report "DEBUG execute-version: Setting g_moduleDefault($modparent) $ModulesVersion" - } - - # Keep track of rc files we already sourced so we don't run them again - set g_rcfilesSourced($modfile) $ModulesVersion - } - return $g_rcfilesSourced($modfile) -} - - -######################################################################## -# commands run from inside a module file -# -set ModulesCurrentModulefile {} - -proc module-info {what {more {}}} { - global g_shellType g_shell g_debug tcl_platform - global g_moduleAlias g_symbolHash g_versionHash - - set mode [currentMode] - - if {$g_debug} { - report "DEBUG module-info: $what $more mode=$mode" - } - - switch -- $what { - "mode" { - if {$more != ""} { - if {$mode == $more} { - return 1 - } else { - return 0 - } - } else { - return $mode - } - } - "name" - - "specified" { - return [currentModuleName] - } - "shell" { - return $g_shell - } - "flags" { - return 0 - } - "shelltype" { - return $g_shellType - } - "user" { - return $tcl_platform(user) - } - "alias" { - if {[info exists g_moduleAlias($more)]} { - return $g_moduleAlias($more) - } else { - return {} - } - } - "trace" { - return {} - } - "tracepat" { - return {} - } - "symbols" { - if {[regexp {^\/} $more]} { - set tmp [currentModuleName] - set tmp [file dirname $tmp] - set more "${tmp}$more" - } - if {[info exists g_symbolHash($more)]} { - return $g_symbolHash($more) - } else { - return {} - } - } - "version" { - if {[regexp {^\/} $more]} { - set tmp [currentModuleName] - set tmp [file dirname $tmp] - set more "${tmp}$more" - } - if {[info exists g_versionHash($more)]} { - return $g_versionHash($more) - } else { - return {} - } - } - default { - error "module-info $what not supported" - return {} - } - } -} - -proc module-whatis {message} { - global g_whatis g_debug - - set mode [currentMode] - - if {$g_debug} { - report "DEBUG module-whatis: $message mode=$mode" - } - - if {$mode == "display"} { - report "module-whatis\t$message" - } else { - set g_whatis $message - } - return {} -} - -# Specifies a default or alias version for a module that points to an -# existing module version Note that the C version stores aliases and -# defaults by the short module name (not the full path) so aliases and -# defaults from one directory will apply to modules of the same name found -# in other directories. -proc module-version {args} { - global g_moduleVersion g_versionHash - global g_moduleDefault - global g_debug - global ModulesCurrentModulefile - - if {$g_debug} { - report "DEBUG module-version: executing module-version $args" - } - set module_name [lindex $args 0] - - # Check for shorthand notation of just a version "/version". Base is - # implied by current dir prepend the current directory to module_name - if {[regexp {^\/} $module_name]} { - set base [file dirname $ModulesCurrentModulefile] - set module_name "${base}$module_name" - } - - foreach version [lrange $args 1 end] { - - set base [file dirname $module_name] - set aliasversion [file tail $module_name] - - if {$base == ""} { - error "module-version: module argument for default must not be fully version qualified" - } - if {[string match $version "default"]} { - # If we see more than one default for the same module, just - # keep the first - if {![info exists g_moduleDefault($base)]} { - set g_moduleDefault($base) $aliasversion - if {$g_debug} { - report "DEBUG module-version: default $base = $aliasversion" - } - } - } else { - set aliasversion "$base/$version" - if {$g_debug} { - report "DEBUG module-version: alias $aliasversion = $module_name" - } - set g_moduleVersion($aliasversion) $module_name - - if {[info exists g_versionHash($module_name)]} { - # don't add duplicates - if {[lsearch -exact $g_versionHash($module_name)\ - $aliasversion] < 0} { - set tmplist $g_versionHash($module_name) - set tmplist [linsert $tmplist end $aliasversion] - set g_versionHash($module_name) $tmplist - } - } else { - set g_versionHash($module_name) $aliasversion - } - } - - if {$g_debug} { - report "DEBUG module-version: $aliasversion = $module_name" - } - } - if {[string match [currentMode] "display"]} { - report "module-version\t$args" - } - return {} -} - - -proc module-alias {args} { - global g_moduleAlias - global ModulesCurrentModulefile - global g_debug - - set alias [lindex $args 0] - set module_file [lindex $args 1] - - if {$g_debug} { - report "DEBUG module-alias: $alias = $module_file" - } - - set g_moduleAlias($alias) $module_file - - if {[info exists g_aliasHash($module_file)]} { - set tmplist $g_aliasHash($module_file) - set tmplist [linsert $tmplist end $alias] - set g_aliasHash($module_file) $tmplist - } else { - set g_aliasHash($module_file) $alias - } - - if {[string match [currentMode] "display"]} { - report "module-alias\t$args" - } - - - return {} -} - - -proc module {command args} { - set mode [currentMode] - global g_debug - - # Resolve any module aliases - if {$g_debug} { - report "DEBUG module: Resolving $args" - } - set args [resolveModuleVersionOrAlias $args] - if {$g_debug} { - report "DEBUG module: Resolved to $args" - } - - switch -- $command { - add - lo - - load { - if {$mode == "load"} { - eval cmdModuleLoad $args - }\ - elseif {$mode == "unload"} { - eval cmdModuleUnload $args - }\ - elseif {$mode == "display"} { - report "module load\t$args" - } - } - rm - unlo - - unload { - if {$mode == "load"} { - eval cmdModuleUnload $args - }\ - elseif {$mode == "unload"} { - eval cmdModuleUnload $args - }\ - elseif {$mode == "display"} { - report "module unload\t$args" - } - } - reload { - cmdModuleReload - } - use { - eval cmdModuleUse $args - } - unuse { - eval cmdModuleUnuse $args - } - source { - eval cmdModuleSource $args - } - switch - - swap { - eval cmdModuleSwitch $args - } - display - dis - - show { - eval cmdModuleDisplay $args - } - avail - av { - if {$args != ""} { - foreach arg $args { - cmdModuleAvail $arg - } - } else { - cmdModuleAvail - # Not sure if this should be a part of cmdModuleAvail or not - cmdModuleAliases - } - } - aliases - al { - cmdModuleAliases - } - path { - eval cmdModulePath $args - } - paths { - eval cmdModulePaths $args - } - list { - cmdModuleList - } - whatis { - if {$args != ""} { - foreach arg $args { - cmdModuleWhatIs $arg - } - } else { - cmdModuleWhatIs - } - } - apropos - search - - keyword { - eval cmdModuleApropos $args - } - purge { - eval cmdModulePurge - } - initadd { - eval cmdModuleInit add $args - } - initprepend { - eval cmdModuleInit prepend $args - } - initrm { - eval cmdModuleInit rm $args - } - initlist { - eval cmdModuleInit list $args - } - initclear { - eval cmdModuleInit clear $args - } - default { - error "module $command not understood" - } - } - return {} -} - -proc setenv {var val} { - global g_stateEnvVars env g_debug - set mode [currentMode] - - if {$g_debug} { - report "DEBUG setenv: ($var,$val) mode = $mode" - } - - if {$mode == "load"} { - set env($var) $val - set g_stateEnvVars($var) "new" - } elseif {$mode == "unload"} { - # Don't unset-env here ... it breaks modulefiles - # that use env(var) is later in the modulefile - #unset-env $var - set g_stateEnvVars($var) "del" - } elseif {$mode == "display"} { - # Let display set the variable for later use in the display - # but don't commit it to the env - set env($var) $val - set g_stateEnvVars($var) "nop" - report "setenv\t\t$var\t$val" - } - return {} -} - -proc getenv {var} { - global g_debug - set mode [currentMode] - - if {$g_debug} { - report "DEBUG getenv: ($var) mode = $mode" - } - - if {$mode == "load" || $mode == "unload"} { - if {[info exists env($var)]} { - return $::env($var) - } else { - return "_UNDEFINED_" - } - } elseif {$mode == "display"} { - return "\$$var" - } - return {} -} - -proc unsetenv {var {val {}}} { - global g_stateEnvVars env g_debug - set mode [currentMode] - - if {$g_debug} { - report "DEBUG unsetenv: ($var,$val) mode = $mode" - } - - if {$mode == "load"} { - if {[info exists env($var)]} { - unset-env $var - } - set g_stateEnvVars($var) "del" - } elseif {$mode == "unload"} { - if {$val != ""} { - set env($var) $val - set g_stateEnvVars($var) "new" - } - } elseif {$mode == "display"} { - report "unsetenv\t\t$var" - } - return {} -} - -######################################################################## -# path fiddling - -proc getReferenceCountArray {var separator} { - global env g_force g_def_separator g_debug - - if {$g_debug} { - report "DEBUG getReferenceCountArray: ($var, $separator)" - } - - set sharevar "${var}_modshare" - set modshareok 1 - if {[info exists env($sharevar)]} { - if {[info exists env($var)]} { - set modsharelist [split $env($sharevar) $g_def_separator] - set temp [expr {[llength $modsharelist] % 2}] - if {$temp == 0} { - array set countarr $modsharelist - - # sanity check the modshare list - array set fixers {} - array set usagearr {} - foreach dir [split $env($var) $separator] { - set usagearr($dir) 1 - } - foreach path [array names countarr] { - if {! [info exists usagearr($path)]} { - unset countarr($path) - set fixers($path) 1 - } - } - - foreach path [array names usagearr] { - if {! [info exists countarr($path)]} { - set countarr($path) 999999999 - } - } - - if {! $g_force} { - if {[array size fixers]} { - reportWarning "WARNING: \$$var does not agree with\ - \$${var}_modshare counter. The following\ - directories' usage counters were adjusted to match.\ - Note that this may mean that module unloading may\ - not work correctly." - foreach dir [array names fixers] { - reportWarning " $dir" -nonewline - } - reportWarning "" - } - } - - } else { - # sharevar was corrupted, odd number of elements. - set modshareok 0 - } - } else { - if {$g_debug} { - reportWarning "WARNING: module: $sharevar exists (\ - $env($sharevar) ), but $var doesn't. Environment is corrupted." - } - set modshareok 0 - } - } else { - set modshareok 0 - } - - if {$modshareok == 0 && [info exists env($var)]} { - array set countarr {} - foreach dir [split $env($var) $separator] { - set countarr($dir) 1 - } - } - return [array get countarr] -} - - -proc unload-path {var path separator} { - global g_stateEnvVars env g_force g_def_separator g_debug - - array set countarr [getReferenceCountArray $var $separator] - - if {$g_debug} { - report "DEBUG unload-path: ($var, $path, $separator)" - } - - # Don't worry about dealing with this variable if it is already scheduled - # for deletion - if {[info exists g_stateEnvVars($var)] && $g_stateEnvVars($var) == "del"} { - return {} - } - - foreach dir [split $path $separator] { - set doit 0 - - if {[info exists countarr($dir)]} { - incr countarr($dir) -1 - if {$countarr($dir) <= 0} { - set doit 1 - unset countarr($dir) - } - } else { - set doit 1 - } - - if {$doit || $g_force} { - if {[info exists env($var)]} { - set dirs [split $env($var) $separator] - set newpath "" - foreach elem $dirs { - if {$elem != $dir} { - lappend newpath $elem - } - } - if {$newpath == ""} { - unset-env $var - set g_stateEnvVars($var) "del" - } else { - set env($var) [join $newpath $separator] - set g_stateEnvVars($var) "new" - } - } - } - } - - set sharevar "${var}_modshare" - if {[array size countarr] > 0} { - set env($sharevar) [join [array get countarr] $g_def_separator] - set g_stateEnvVars($sharevar) "new" - } else { - unset-env $sharevar - set g_stateEnvVars($sharevar) "del" - } - return {} -} - -proc add-path {var path pos separator} { - global env g_stateEnvVars g_def_separator g_debug - - if {$g_debug} { - report "DEBUG add-path: ($var, $path, $separator)" - } - - set sharevar "${var}_modshare" - array set countarr [getReferenceCountArray $var $separator] - - if {$pos == "prepend"} { - set pathelems [reverseList [split $path $separator]] - } else { - set pathelems [split $path $separator] - } - foreach dir $pathelems { - if {[info exists countarr($dir)]} { - # already see $dir in $var" - incr countarr($dir) - } else { - if {[info exists env($var)]} { - if {$pos == "prepend"} { - set env($var) "$dir$separator$env($var)" - } elseif {$pos == "append"} { - set env($var) "$env($var)$separator$dir" - } else { - error "add-path doesn't support $pos" - } - } else { - set env($var) "$dir" - } - set countarr($dir) 1 - } - if {$g_debug} { - report "DEBUG add-path: env($var) = $env($var)" - } - } - - - set env($sharevar) [join [array get countarr] $g_def_separator] - set g_stateEnvVars($var) "new" - set g_stateEnvVars($sharevar) "new" - return {} -} - -proc prepend-path {var path args} { - global g_def_separator g_debug - - set mode [currentMode] - - if {$g_debug} { - report "DEBUG prepend-path: ($var, $path, $args) mode=$mode" - } - - if {[string match $var "-delim"]} { - set separator $path - set var [lindex $args 0] - set path [lindex $args 1] - } else { - set separator $g_def_separator - } - - if {$mode == "load"} { - add-path $var $path "prepend" $separator - } elseif {$mode == "unload"} { - unload-path $var $path $separator - } elseif {$mode == "display"} { - report "prepend-path\t$var\t$path" - } - return {} -} - - -proc append-path {var path args} { - global g_def_separator g_debug - - set mode [currentMode] - - if {$g_debug} { - report "DEBUG append-path: ($var, $path, $args) mode=$mode" - } - - if {[string match $var "-delim"]} { - set separator $path - set var [lindex $args 0] - set path [lindex $args 1] - } else { - set separator $g_def_separator - } - - if {$mode == "load"} { - add-path $var $path "append" $separator - } elseif {$mode == "unload"} { - unload-path $var $path $separator - } elseif {$mode == "display"} { - report "append-path\t$var\t$path" - } - return {} -} - -proc remove-path {var path args} { - global g_def_separator g_debug - - set mode [currentMode] - - if {$g_debug} { - report "DEBUG remove-path: ($var, $path, $args) mode=$mode" - } - - if {[string match $var "-delim"]} { - set separator $path - set var [lindex $args 0] - set path [lindex $args 1] - } else { - set separator $g_def_separator - } - - if {$mode == "load"} { - unload-path $var $path $separator - } elseif {$mode == "display"} { - report "remove-path\t$var\t$path" - } - return {} -} - -proc set-alias {alias what} { - global g_Aliases g_stateAliases g_debug - set mode [currentMode] - - if {$g_debug} { - report "DEBUG set-alias: ($alias, $what) mode=$mode" - } - if {$mode == "load"} { - set g_Aliases($alias) $what - set g_stateAliases($alias) "new" - } elseif {$mode == "unload"} { - set g_Aliases($alias) {} - set g_stateAliases($alias) "del" - } elseif {$mode == "display"} { - report "set-alias\t$alias\t$what" - } - return {} -} - - -proc unset-alias {alias} { - global g_Aliases g_stateAliases g_debug - set mode [currentMode] - - if {$g_debug} { - report "DEBUG unset-alias: ($alias) mode=$mode" - } - if {$mode == "load"} { - set g_Aliases($alias) {} - set g_stateAliases($alias) "del" - } elseif {$mode == "display"} { - report "unset-alias\t$alias" - } - return {} -} - -proc is-loaded {modulelist} { - global env g_def_separator g_debug - - if {$g_debug} { - report "DEBUG is-loaded: $modulelist" - } - - if {[llength $modulelist] > 0} { - if {[info exists env(LOADEDMODULES)]} { - foreach arg $modulelist { - set arg "$arg/" - foreach mod [split $env(LOADEDMODULES) $g_def_separator] { - set mod "$mod/" - if {[string first $arg $mod] == 0} { - return 1 - } - } - } - return 0 - } else { - return 0 - } - } - return 1 -} - - -proc conflict {args} { - global ModulesCurrentModulefile g_debug - set mode [currentMode] - set currentModule [currentModuleName] - - if {$g_debug} { - report "DEBUG conflict: ($args) mode = $mode" - } - - if {$mode == "load"} { - foreach mod $args { - # If the current module is already loaded, we can proceed - if {![is-loaded $currentModule]} { - # otherwise if the conflict module is loaded, we cannot - if {[is-loaded $mod]} { - set errMsg "WARNING: $currentModule cannot be loaded due\ - to a conflict." - set errMsg "$errMsg\nHINT: Might try \"module unload\ - $mod\" first." - error $errMsg - } - } - } - } elseif {$mode == "display"} { - report "conflict\t$args" - } - return {} -} - -proc prereq {args} { - global g_debug - set mode [currentMode] - set currentModule [currentModuleName] - - if {$g_debug} { - report "DEBUG prereq: ($args) mode = $mode" - } - - if {$mode == "load"} { - if {![is-loaded $args]} { - set errMsg "WARNING: $currentModule cannot be loaded due to missing prereq." - set errMsg "$errMsg\nHINT: the following modules must be loaded first: $args" - error $errMsg - } - } elseif {$mode == "display"} { - report "prereq\t\t$args" - } - return {} -} - -proc x-resource {resource {value {}}} { - global g_newXResources g_delXResources g_debug - set mode [currentMode] - - if {$g_debug} { - report "DEBUG x-resource: ($resource, $value)" - } - - if {$mode == "load"} { - set g_newXResources($resource) $value - } elseif {$mode =="unload"} { - set g_delXResources($resource) 1 - } elseif {$mode == "display"} { - report "x-resource\t$resource\t$value" - } - return {} -} - -proc uname {what} { - global unameCache tcl_platform g_debug - set result {} - - if {$g_debug} { - report "DEBUG uname: called: $what" - } - - if {! [info exists unameCache($what)]} { - switch -- $what { - sysname { - set result $tcl_platform(os) - } - machine { - set result $tcl_platform(machine) - } - nodename - - node { - set result [info hostname] - } - release { - # on ubuntu get the CODENAME of the Distribution - if { [file isfile /etc/lsb-release]} { - set fd [open "/etc/lsb-release" "r"] - set a [read $fd] - regexp -nocase {DISTRIB_CODENAME=(\S+)(.*)} $a matched res end - set result $res - } else { - set result $tcl_platform(osVersion) - } - } - domain { - set result [exec /bin/domainname] - } - version { - set result [exec /bin/uname -v] - } - default { - error "uname $what not supported" - } - } - set unameCache($what) $result - } - - return $unameCache($what) -} - -######################################################################## -# internal module procedures - -set g_modeStack {} - -proc currentMode {} { - global g_modeStack - - set mode [lindex $g_modeStack end] - return $mode -} - -proc pushMode {mode} { - global g_modeStack - - lappend g_modeStack $mode -} - -proc popMode {} { - global g_modeStack - - set len [llength $g_modeStack] - set len [expr {$len - 2}] - set g_modeStack [lrange $g_modeStack 0 $len] -} - - -set g_moduleNameStack {} - -proc currentModuleName {} { - global g_moduleNameStack - - set moduleName [lindex $g_moduleNameStack end] - return $moduleName -} - -proc pushModuleName {moduleName} { - global g_moduleNameStack - - lappend g_moduleNameStack $moduleName -} - -proc popModuleName {} { - global g_moduleNameStack - - set len [llength $g_moduleNameStack] - set len [expr {$len - 2}] - set g_moduleNameStack [lrange $g_moduleNameStack 0 $len] -} - - -# Return the full pathname and modulename to the module. -# Resolve aliases and default versions if the module name is something like -# "name/version" or just "name" (find default version). -proc getPathToModule {mod {separator {}}} { - global env g_loadedModulesGeneric - global g_moduleAlias g_moduleVersion - global g_debug g_def_separator - global ModulesCurrentModulefile flag_default_mf flag_default_dir - - set retlist "" - - if {$mod == ""} { - return "" - } - - if {$separator == "" } { - set separator $g_def_separator - } - - if {$g_debug} { - report "DEBUG getPathToModule: Finding $mod" - } - - # Check for aliases - # This is already done at the root level so why do it again? - # set newmod [resolveModuleVersionOrAlias $mod] - # if {$newmod != $mod} { - # # Alias before ModulesVersion - # return [getPathToModule $newmod] - # } - - # Check for $mod specified as a full pathname - if {[string match {/*} $mod]} { - if {[file exists $mod]} { - if {[file readable $mod]} { - if {[file isfile $mod]} { - # note that a raw filename as an argument returns the full - # path as the module name - if {[checkValidModule $mod]} { - return [list $mod $mod] - } else { - report "+(0):ERROR:0: Unable to locate a modulefile for '$mod'" - return "" - } - } - } - } - } elseif {![info exists env(MODULEPATH)]} { - error "\$MODULEPATH not defined" - return "" - } - # Now search for $mod in MODULEPATH - foreach dir [split $env(MODULEPATH) $separator] { - set path "$dir/$mod" - - # modparent is the the modulename minus the module version. - set modparent [file dirname $mod] - set modversion [file tail $mod] - # If $mod was specified without a version (no "/") then mod is - # really modparent - if {$modparent == "."} { - set modparent $mod - } - set modparentpath "$dir/$modparent" - - # Search the modparent directory for .modulerc files in case we - # need to translate an alias - if {[file isdirectory $modparentpath]} { - # Execute any modulerc for this module - if {[file exists "$modparentpath/.modulerc"]} { - if {$g_debug} { - report "DEBUG getPathToModule: Found $modparentpath/.modulerc" - } - execute-modulerc $modparentpath/.modulerc - } - # Check for an alias - set newmod [resolveModuleVersionOrAlias $mod] - if {$newmod != $mod} { - # Alias before ModulesVersion - return [getPathToModule $newmod] - } - } - - # Now check if the mod specified is a file or a directory - if {![file readable $path]} { - continue - } - # If a directory, return the default if a .version file is - # present or return the last file within the dir - if {[file isdirectory $path]} { - set ModulesVersion "" - # Not an alias or version alias - check for a .version\ - file or find the default file - if {[info exists g_loadedModulesGeneric($mod)]} { - set ModulesVersion $g_loadedModulesGeneric($mod) - } elseif {[file exists "$path/.version"] && ![file readable "$path/.modulerc"]} { - # .version files aren't read if .modulerc present - if {$g_debug} { - report "DEBUG getPathToModule: Found $path/.version" - } - set ModulesVersion [execute-modulerc "$path/.version"] - } - - - # Try for the last file in directory if no luck so far - if {$ModulesVersion == ""} { - set modlist [listModules $path "" 0 "-dictionary" 0 0] - set ModulesVersion [lindex $modlist end] - if {$g_debug} { - report "DEBUG getPathToModule: Found $ModulesVersion in $path" - } - } - - - if {$ModulesVersion != ""} { - # The path to the module file - set verspath "$path/$ModulesVersion" - # The modulename (name + version) - set versmod "$mod/$ModulesVersion" - set retlist [list $verspath $versmod] - } - } else { - # If mod was a file in this path, try and return that file - set retlist [list $path $mod] - } - - # We may have a winner, check validity of result - if {[llength $retlist] == 2} { - # Check to see if we've found only a directory. If so, - # keep looking - if {[file isdirectory [lindex $retlist 0]]} { - set retlist [getPathToModule [lindex $retlist 1]] - } - - if {! [checkValidModule [lindex $retlist 0]]} { - set path [lindex $retlist 0] - } else { - return $retlist - } - } - } - # End of of foreach loop - report "+(0):ERROR:0: Unable to locate a modulefile for '$mod'" - return "" -} - -proc runModulerc {} { - # Runs the global RC files if they exist - global env g_debug - - if {$g_debug} { - report "DEBUG runModulerc: running..." - report "DEBUG runModulerc: env MODULESHOME = $env(MODULESHOME)" - report "DEBUG runModulerc: env HOME = $env(HOME)" - } - if {[info exists env(MODULERCFILE)]} { - if {[file readable $env(MODULERCFILE)]} { - if {$g_debug} { - report "DEBUG runModulerc: Executing $env(MODULERCFILE)" - } - cmdModuleSource $env(MODULERCFILE) - } - } - if {[info exists env(MODULESHOME)]} { - if {[file readable "$env(MODULESHOME)/etc/rc"]} { - if {$g_debug} { - report "DEBUG runModulerc: Executing $env(MODULESHOME)/etc/rc" - } - cmdModuleSource "$env(MODULESHOME)/etc/rc" - } - } - if {[info exists env(HOME)]} { - if {[file readable "$env(HOME)/.modulerc"]} { - if {$g_debug} { - report "DEBUG runModulerc: Executing $env(HOME)/.modulerc" - } - cmdModuleSource "$env(HOME)/.modulerc" - } - } -} - -proc saveSettings {} { - foreach var {env g_Aliases g_stateEnvVars g_stateAliases g_newXResource g_delXResource} { - eval "global g_SAVE_$var $var" - eval "array set g_SAVE_$var \[array get $var\]" - } -} - -proc restoreSettings {} { - foreach var {env g_Aliases g_stateEnvVars g_stateAliases g_newXResource g_delXResource} { - eval "global g_SAVE_$var $var" - eval "array set $var \[array get g_SAVE_$var\]" - } -} - -proc renderSettings {} { - global env g_Aliases g_shellType g_shell - global g_stateEnvVars g_stateAliases - global g_newXResources g_delXResources - global g_pathList g_systemList error_count - global g_autoInit CSH_LIMIT g_debug - - if {$g_debug} { - report "DEBUG renderSettings: called." - } - - set iattempt 0 - - # required to work on cygwin, shouldn't hurt real linux - fconfigure stdout -translation lf - - # preliminaries - - switch -- $g_shellType { - python { - puts stdout "import os" - } - } - - if {$g_autoInit} { - global argv0 - - # automatically detect which tclsh should be used for future module commands - set tclshbin [info nameofexecutable] - - # add cwd if not absolute script path - if {! [regexp {^/} $argv0]} { - set pwd [exec pwd] - set argv0 "$pwd/$argv0" - } - - set env(MODULESHOME) [file dirname $argv0] - set g_stateEnvVars(MODULESHOME) "new" - - switch -- $g_shellType { - csh { - puts stdout "if ( \$?histchars ) then" - puts stdout " set _histchars = \$histchars" - puts stdout " if (\$?prompt) then" - puts stdout " alias module 'unset histchars;set\ - _prompt=\"\$prompt\";eval `'$tclshbin' '$argv0' '$g_shell' \\!*`;set\ - histchars = \$_histchars; set prompt=\"\$_prompt\";unset\ - _prompt'" - puts stdout " else" - puts stdout " alias module 'unset histchars;eval `'$tclshbin' '$argv0'\ - '$g_shell' \\!*`;set histchars = \$_histchars'" - puts stdout " endif" - puts stdout "else" - puts stdout " if (\$?prompt) then" - puts stdout " alias module 'set _prompt=\"\$prompt\";set\ - prompt=\"\";eval `'$tclshbin' '$argv0' '$g_shell' \\!*`;set\ - prompt=\"\$_prompt\";unset _prompt'" - puts stdout " else" - puts stdout " alias module 'eval `'$tclshbin' '$argv0' '$g_shell' \\!*`'" - puts stdout " endif" - puts stdout "endif" - } - sh { - puts stdout "module () { eval `'$tclshbin' '$argv0' '$g_shell' \$*`; } ;" - } - cmd { - puts stdout "start /b \%MODULESHOME\%/init/module.cmd %*" - } - perl { - puts stdout "sub module {" - puts stdout " eval `$tclshbin \$ENV{\'MODULESHOME\'}/modulecmd.tcl perl @_`;" - puts stdout " if(\$@) {" - puts stdout " use Carp;" - puts stdout " confess \"module-error: \$@\n\";" - puts stdout " }" - puts stdout " return 1;" - puts stdout "}" - } - python { - puts stdout "import subprocess" - puts stdout "def module(command, *arguments):" - puts stdout " exec subprocess.Popen(\['$tclshbin', '$argv0', 'python', command\] \ - list(arguments), stdout=subprcess.PIPE).communicate()\[0\]" - } - lisp { - error "ERROR: XXX lisp mode autoinit not yet implemented" - } - } - - if {[file exists "$env(MODULESHOME)/modulerc"]} { - cmdModuleSource "$env(MODULESHOME)/modulerc" - } - if {[file exists "$env(MODULESHOME)/init/modulerc"]} { - cmdModuleSource "$env(MODULESHOME)/init/modulerc" - } - } - - - # new environment variables - foreach var [array names g_stateEnvVars] { - if {$g_stateEnvVars($var) == "new"} { - switch -- $g_shellType { - csh { - set val [multiEscaped $env($var)] - # csh barfs on long env vars - if {$g_shell == "csh" && [string length $val] >\ - $CSH_LIMIT} { - if {$var == "PATH"} { - reportWarning "WARNING: module: PATH exceeds\ - $CSH_LIMIT characters, truncating and\ - appending /usr/bin:/bin ..." - set val [string range $val 0 [expr {$CSH_LIMIT\ - - 1}]]:/usr/bin:/bin - } else { - reportWarning "WARNING: module: $var exceeds\ - $CSH_LIMIT characters, truncating..." - set val [string range $val 0 [expr {$CSH_LIMIT\ - - 1}]] - } - } - puts stdout "setenv $var $val;" - } - sh { - puts stdout "$var=[multiEscaped $env($var)]; export $var;" - } - perl { - set val [doubleQuoteEscaped $env($var)] - set val [atSymbolEscaped $env($var)] - puts stdout "\$ENV{\'$var\'} = \'$val\';" - } - python { - set val [singleQuoteEscaped $env($var)] - puts stdout "os.environ\['$var'\] = '$val'" - } - lisp { - set val [doubleQuoteEscaped $env($var)] - puts stdout "(setenv \"$var\" \"$val\")" - } - cmd { - set val $env($var) - puts stdout "set $var=$val" - } - } - } elseif {$g_stateEnvVars($var) == "del"} { - switch -- $g_shellType { - csh { - puts stdout "unsetenv $var;" - } - sh { - puts stdout "unset $var;" - } - cmd { - puts stdout "set $var=" - } - perl { - puts stdout "delete \$ENV{\'$var\'};" - } - python { - puts stdout "os.environ\['$var'\] = ''" - puts stdout "del os.environ\['$var'\]" - } - lisp { - puts stdout "(setenv \"$var\" nil)" - } - } - } - } - - foreach var [array names g_stateAliases] { - if {$g_stateAliases($var) == "new"} { - switch -- $g_shellType { - csh { - # set val [multiEscaped $g_Aliases($var)] - set val $g_Aliases($var) - # Convert $n -> \!\!:n - regsub -all {\$([0-9]+)} $val {\\!\\!:\1} val - # Convert $* -> \!* - regsub -all {\$\*} $val {\\!*} val - puts stdout "alias $var '$val';" - } - sh { - set val $g_Aliases($var) - puts stdout "alias $var=\'$val\';" - } - } - } elseif {$g_stateAliases($var) == "del"} { - switch -- $g_shellType { - csh { - puts stdout "unalias $var;" - } - sh { - puts stdout "unalias $var;" - } - } - } - } - - # new x resources - if {[array size g_newXResources] > 0} { - set xrdb [findExecutable "xrdb"] - foreach var [array names g_newXResources] { - set val $g_newXResources($var) - if {$val == ""} { - switch -regexp -- $g_shellType { - {^(csh|sh)$} { - if {[file exists $var]} { - puts stdout "$xrdb -merge $var;" - } else { - puts stdout "$xrdb -merge < 0} { - set xrdb [findExecutable "xrdb"] - foreach var [array names g_delXResources] { - if {$val == ""} { - # do nothing - } else { - puts stdout "xrdb -remove < 0} { - reportWarning "ERROR: $error_count error(s) detected." - switch -- $g_shellType { - csh { - puts stdout "/bin/false;" - } - sh { - puts stdout "/bin/false;" - } - cmd { - # nothing needed, reserve for future cygwin, MKS, etc - } - perl { - puts stdout "die \"modulefile.tcl: $error_count error(s) detected!\\n\"" - } - python { - puts stdout "raise RuntimeError, 'modulefile.tcl: $error_count error(s) detected!'" - } - lisp { - puts stdout "(error \"modulefile.tcl: $error_count error(s) detected!\")" - } - } - set nop 0 - } else { - switch -- $g_shellType { - perl { - puts stdout "1;" - } - } - } - - - if {$nop} { - # nothing written! - switch -- $g_shellType { - csh { - puts "/bin/true;" - } - sh { - puts "/bin/true;" - } - cmd { - # nothing needed, reserve for future cygwin, MKS, etc - } - perl { - puts "1;" - } - python { - # this is not correct - puts "" - } - lisp { - puts "t" - } - } - } else { - } -} - -proc cacheCurrentModules {{separator {}}} { - global g_loadedModules g_loadedModulesGeneric env g_def_separator g_debug - - if {$g_debug} { - report "DEBUG cacheCurrentModules: ($separator)" - } - - if {$separator == "" } { - set separator $g_def_separator - } - - # mark specific as well as generic modules as loaded - if {[info exists env(LOADEDMODULES)]} { - foreach mod [split $env(LOADEDMODULES) $separator] { - set g_loadedModules($mod) 1 - set g_loadedModulesGeneric([file dirname $mod]) [file tail $mod] - } - } -} - -# This proc resolves module aliases or version aliases to the real module name -# and version -proc resolveModuleVersionOrAlias {names} { - global g_moduleVersion g_moduleDefault g_moduleAlias g_debug - - if {$g_debug} { - report "DEBUG resolveModuleVersionOrAlias: Resolving $names" - } - set ret_list {} - - foreach name $names { - # Chop off (default) if it exists - set x [expr {[string length $name] - 9}] - if {($x > 0) &&([string range $name $x end] == "\(default\)")} { - set name [string range $name 0 [expr {$x -1}]] - if {$g_debug} { - report "DEBUG resolveModuleVersionOrAlias: trimming name = \"$name\"" - } - } - if {[info exists g_moduleAlias($name)]} { - # if the alias is another alias, we need to resolve it - if {$g_debug} { - report "DEBUG resolveModuleVersionOrAlias: $name is an alias" - } - set ret_list [linsert $ret_list end\ - [resolveModuleVersionOrAlias $g_moduleAlias($name)]] - } elseif {[info exists g_moduleVersion($name)]} { - # if the pseudo version is an alias, we need to resolve it - if {$g_debug} { - report "DEBUG resolveModuleVersionOrAlias: $name is a version alias" - } - set ret_list [linsert $ret_list end\ - [resolveModuleVersionOrAlias $g_moduleVersion($name)]] - } elseif {[info exists g_moduleDefault($name)]} { - # if the default is an alias, we need to resolve it - if {$g_debug} { - report "DEBUG resolveModuleVersionOrAlias: found a default for $name" - } - set ret_list [linsert $ret_list end [resolveModuleVersionOrAlias\ - "$name/$g_moduleDefault($name)"]] - } else { - if {$g_debug} { - report "DEBUG resolveModuleVersionOrAlias: $name is nothing special" - } - set ret_list [linsert $ret_list end $name] - } - } - if {$g_debug} { - report "DEBUG resolveModuleVersionOrAlias: Resolved to $ret_list" - } - return $ret_list -} - -proc spaceEscaped {text} { - regsub -all " " $text "\\ " regsub_tmpstrg - return $regsub_tmpstrg -} - -proc multiEscaped {text} { - regsub -all {([ \\\t\{\}|<>!;#^$&*"'`()])} $text {\\\1} regsub_tmpstrg - return $regsub_tmpstrg -} - -proc doubleQuoteEscaped {text} { - regsub -all "\"" $text "\\\"" regsub_tmpstrg - return $regsub_tmpstrg -} - -proc atSymbolEscaped {text} { - regsub -all "@" $text "\\@" regsub_tmpstrg - return $regsub_tmpstrg -} - -proc singleQuoteEscaped {text} { - regsub -all "\'" $text "\\\'" regsub_tmpstrg - return $regsub_tmpstrg -} - -proc findExecutable {cmd} { - foreach dir {/usr/X11R6/bin /usr/openwin/bin /usr/bin/X11} { - if {[file executable "$dir/$cmd"]} { - return "$dir/$cmd" - } - } - return $cmd -} - -proc reverseList {list} { - set newlist {} - - foreach item $list { - set newlist [linsert $newlist 0 $item] - } - return $newlist -} - -proc replaceFromList {list1 item {item2 {}}} { - set xi [lsearch -exact $list1 $item] - - while {$xi >= 0} { - if {[string length $item2] == 0} { - set list1 [lreplace $list1 $xi $xi] - } else { - set list1 [lreplace $list1 $xi $xi $item2] - } - set xi [lsearch -exact $list1 $item] - } - - return $list1 -} - -proc checkValidModule {modfile} { - global g_debug - - if {$g_debug} { - report "DEBUG checkValidModule: $modfile" - } - - # Check for valid module - if {![catch {open $modfile r} fileId]} { - gets $fileId first_line - close $fileId - if {[string first "\#%Module" $first_line] == 0} { - return 1 - } - } - return 0 -} - -# If given module maps to default or other version aliases, a list of -# those aliases is returned. This takes the full path to a module as -# an argument. -proc getVersAliasList {modulename} { - global g_versionHash g_moduleDefault g_debug - - if {$g_debug} { - report "DEBUG getVersAliasList: $modulename" - } - - set modparent [file dirname $modulename] - - set tag_list {} - if {[info exists g_versionHash($modulename)]} { - # remove module basenames to get just version names - foreach version $g_versionHash($modulename) { - set alias_tag [file tail $version] - set tag_list [linsert $tag_list end $alias_tag] - } - } - if {[info exists g_moduleDefault($modparent)]} { - set tmp_name "$modparent/$g_moduleDefault($modparent)" - if {$tmp_name == $modulename} { - set tag_list [linsert $tag_list end "default"] - } - } - return $tag_list -} - -# Finds all module versions for mod in the module path dir -proc listModules {dir mod {full_path 1} {sort_order {-dictionary}}\ - {flag_default_mf {1}} {flag_default_dir {1}}} { - global ignoreDir - global ModulesCurrentModulefile - global g_debug - global tcl_platform - global g_versionHash - - # On Cygwin, glob may change the $dir path if there are symlinks involved - # So it is safest to reglob the $dir. - # example: - # [glob /home/stuff] -> "//homeserver/users0/stuff" - - set dir [glob $dir] - set full_list [glob -nocomplain "$dir/$mod"] - - # remove trailing / needed on some platforms - regsub {\/$} $full_list {} full_list - - set clean_list {} - set ModulesVersion {} - for {set i 0} {$i < [llength $full_list]} {incr i 1} { - set element [lindex $full_list $i] - set tag_list {} - - # Cygwin TCL likes to append ".lnk" to the end of symbolic links. - # This is not necessary and pollutes the module names, so let's - # trim it off. - if { [isWin] } { - regsub {\.lnk$} $element {} element - } - - set tail [file tail $element] - set direlem [file dirname $element] - - set sstart [expr {[string length $dir] +1}] - set modulename [string range $element $sstart end] - - - if {[file isdirectory $element] && [file readable $element]} { - set ModulesVersion "" - - if {$g_debug} { - report "DEBUG listModules: found $element" - } - - if {![info exists ignoreDir($tail)]} { - # include .modulerc or if not present .version file - if {[file readable $element/.modulerc]} { - lappend full_list $element/.modulerc - } elseif {[file readable $element/.version]} { - lappend full_list $element/.version - } - # Add each element in the current directory to the list - foreach f [glob -nocomplain "$element/*"] { - lappend full_list $f - } - - # if element is directory AND default or a version alias, add - # it to the list - set tag_list [getVersAliasList $element] - - set tag {} - if {[llength $tag_list]} { - append tag "(" [join $tag_list ":"] ")" - - if {$full_path} { - set mystr ${element} - } else { - set mystr ${modulename} - } - if {[file isdirectory ${element}]} { - if {$flag_default_dir} { - set mystr "$mystr$tag" - } - } elseif {$flag_default_mf} { - set mystr "$mystr$tag" - } - lappend clean_list $mystr - } - } - } else { - if {$g_debug} { - report "DEBUG listModules: checking $element ($modulename) dir=$flag_default_dir mf=$flag_default_mf" - } - switch -glob -- $tail { - {.modulerc} { - if {$flag_default_dir || $flag_default_mf} { - # set is needed for execute-modulerc - set ModulesCurrentModulefile $element - execute-modulerc $element - } - } - {.version} { - if {$flag_default_dir || $flag_default_mf} { - # set is needed for execute-modulerc - set ModulesCurrentModulefile $element - execute-modulerc "$element" - - if {$g_debug} { - report "DEBUG listModules: checking default $element" - } - } - } - {.*} - - {*~} - - {*,v} - - {\#*\#} { } - default { - if {[checkValidModule $element]} { - - set tag_list [getVersAliasList $element] - - set tag {} - if {[llength $tag_list]} { - append tag "(" [join $tag_list ":"] ")" - } - if {$full_path} { - set mystr ${element} - } else { - set mystr ${modulename} - } - if {[file isdirectory ${element}]} { - if {$flag_default_dir} { - set mystr "$mystr$tag" - } - } elseif {$flag_default_mf} { - set mystr "$mystr$tag" - } - lappend clean_list $mystr - } - } - } - } - } - if {$sort_order != {}} { - set clean_list [lsort $sort_order $clean_list] - } - if {$g_debug} { - report "DEBUG listModules: Returning $clean_list" - } - - return $clean_list -} - - -proc showModulePath {{separator {}}} { - global env g_def_separator g_debug - - if {$g_debug} { - report "DEBUG showModulePath: $separator" - } - - if {$separator == "" } { - set separator $g_def_separator - } - if {[info exists env(MODULEPATH)]} { - report "Search path for module files (in search order):" - foreach path [split $env(MODULEPATH) $separator] { - report " $path" - - } - } else { - reportWarning "WARNING: no directories on module search path" - } -} - -######################################################################## -# command line commands - -proc cmdModuleList {{separator {}}} { - global env DEF_COLUMNS show_oneperline show_modtimes g_debug - global g_def_separator - - if {$separator == "" } { - set separator $g_def_separator - } - - if {[info exists env(LOADEDMODULES)]} { - set loaded $env(LOADEDMODULES) - } else { - set loaded "" - } - - if { [string length $loaded] == 0} { - report "No Modulefiles Currently Loaded." - return - } - set list {} - report "Currently Loaded Modulefiles:" - set max 0 - - foreach mod [split $loaded $separator] { - set len [string length $mod] - - if {$len > 0} { - - if {$show_oneperline} { - report $mod - } elseif {$show_modtimes} { - set filetime [clock format [file mtime [lindex [getPathToModule $mod] 0]] -format "%Y/%m/%b %H:%M:%S"] - report [format "%-50s%10s" $mod $filetime] - } else { - if {$len > $max} { - set max $len - } - # skip zero length module names - # call getPathToModule to find and execute .version and\ - .modulerc files for this module - getPathToModule $mod - set tag_list [getVersAliasList $mod] - - if {[llength $tag_list]} { - append mod "(" [join $tag_list $separator] ")" - # expand string length to include version alises - set len [string length $mod] - if {$len > $max} { - set max $len - } - } - - lappend list $mod - } - } - } - if {$show_oneperline == 0 && $show_modtimes == 0} { - # save room for numbers and spacing: 2 digits + ) + space + space - set cols [expr {int($DEF_COLUMNS/($max + 5))}] - # safety check to prevent divide by zero error below - if {$cols <= 0} { - set cols 1 - } - - set item_cnt [llength $list] - set rows [expr {int($item_cnt / $cols)}] - set lastrow_item_cnt [expr {int($item_cnt % $cols)}] - if {$lastrow_item_cnt > 0} { - incr rows - } - if {$g_debug} { - report "list = $list" - report "rows/cols = $rows/$cols, max = $max" - report "item_cnt = $item_cnt, lastrow_item_cnt = $lastrow_item_cnt" - } - for {set row 0} {$row < $rows} {incr row} { - for {set col 0} {$col < $cols} {incr col} { - set index [expr {$col * $rows + $row}] - set mod [lindex $list $index] - - if {$mod == ""} { - continue - } - set n [expr {$index +1}] - set mod [format "%2d) %-${max}s " $n $mod] - report $mod -nonewline - } - report "" - } - } -} - -proc cmdModuleDisplay {mod} { - global env tcl_version ModulesCurrentModulefile - - set modfile [getPathToModule $mod] - if {$modfile == ""} { - return - } - pushModuleName [lindex $modfile 1] - set modfile [lindex $modfile 0] - report "-------------------------------------------------------------------" - report "$modfile:\n" - pushMode "display" - execute-modulefile $modfile - popMode - popModuleName - report "-------------------------------------------------------------------" -} - -proc cmdModulePaths {mod {separator {}}} { - global env g_pathList flag_default_mf flag_default_dir - global g_def_separator g_debug - - if {$g_debug} { - report "DEBUG cmdModulePaths: ($mod, $separator)" - } - - if {$separator == "" } { - set separator $g_def_separator - } - - if {[catch { - foreach dir [split $env(MODULEPATH) $separator] { - if {![file isdirectory $dir]} { - continue - } - foreach mod2 [listModules $dir $mod 0 "" $flag_default_mf $flag_default_dir] { - lappend g_pathList $mod2 - } - } - } errMsg]} { - reportWarning "ERROR: module paths $mod failed. $errMsg" - } -} - -proc cmdModulePath {mod} { - global env g_pathList ModulesCurrentModulefile g_debug - - if {$g_debug} { - report "DEBUG cmdModulePath: ($mod)" - } - set modfile [getPathToModule $mod] - if {$modfile == ""} { - return - } - set modfile [lindex $modfile 0] - set ModulesCurrentModulefile $modfile - - set g_pathList $modfile -} - -proc cmdModuleWhatIs {{mod {}}} { - cmdModuleSearch $mod {} -} - -proc cmdModuleApropos {{search {}}} { - cmdModuleSearch {} $search -} - -proc cmdModuleSearch {{mod {}} {search {}}} { - global env tcl_version ModulesCurrentModulefile - global g_whatis g_def_separator g_debug - - if {$g_debug} { - report "DEBUG cmdModuleSearch: ($mod, $search)" - } - if {$mod == ""} { - set mod "*" - } - foreach dir [split $env(MODULEPATH) $g_def_separator] { - if {![file isdirectory $dir]} { - continue - } - report "----------- $dir ------------- " - set modlist [listModules $dir $mod 0 "" 0 0] - foreach mod2 $modlist { - set g_whatis "" - set modfile [getPathToModule $mod2] - if {$modfile == ""} { - continue - } - pushMode "whatis" - pushModuleName [lindex $modfile 1] - set modfile [lindex $modfile 0] - execute-modulefile $modfile - popMode - popModuleName - if {$search =="" || [regexp -nocase $search $g_whatis]} { - report [format "%20s: %s" $mod2 $g_whatis] - } - } - } -} - -proc cmdModuleSwitch {old {new {}}} { - global env g_debug g_loadedModulesGeneric g_loadedModules - - if {$new == ""} { - set new $old - } elseif {[info exists g_loadedModules($new)]} { - set tmp $new - set new $old - set old $tmp - } - - if {![info exists g_loadedModules($old)] && - [info exists g_loadedModulesGeneric($old)]} { - set old "$old/$g_loadedModulesGeneric($old)" - } - - if {$g_debug} { - report "DEBUG cmdModuleSwitch: new=\"$new\" old=\"$old\"" - } - - cmdModuleUnload $old - cmdModuleLoad $new -} - -proc cmdModuleSource {args} { - global env tcl_version g_loadedModules g_loadedModulesGeneric - global g_force g_debug - - if {$g_debug} { - report "DEBUG cmdModuleSource: $args" - } - foreach file $args { - if {![file exists $file]} { - error "File $file does not exist" - } - pushMode "load" - pushModuleName $file - execute-modulefile $file - popModuleName - popMode - } -} - -proc cmdModuleLoad {args} { - global env g_loadedModules g_loadedModulesGeneric g_force - global ModulesCurrentModulefile - global g_debug - - if {$g_debug} { - report "DEBUG cmdModuleLoad: loading $args" - } - - foreach mod $args { - set modfile [getPathToModule $mod] - if {$modfile == ""} { - continue - } - set currentModule [lindex $modfile 1] - set modfile [lindex $modfile 0] - set ModulesCurrentModulefile $modfile - - if {! $g_force && [info exists g_loadedModules($currentModule)]} { - continue - } - pushMode "load" - pushModuleName $currentModule - - saveSettings - if {[execute-modulefile $modfile]} { - restoreSettings - } else { - append-path LOADEDMODULES $currentModule - append-path _LMFILES_ $modfile - set g_loadedModules($currentModule) 1 - set genericModName [file dirname $mod] - - if {![info exists g_loadedModulesGeneric($genericModName)]} { - set g_loadedModulesGeneric($genericModName) [file tail $currentModule] - } - } - - popMode - popModuleName - } -} - -proc cmdModuleUnload {args} { - global tcl_version g_loadedModules g_loadedModulesGeneric - global ModulesCurrentModulefile g_debug g_def_separator - - if {$g_debug} { - report "DEBUG cmdModuleUnload: unloading $args" - } - - foreach mod $args { - if {[catch { - set modfile [getPathToModule $mod] - if {$modfile != ""} { - set currentModule [lindex $modfile 1] - set modfile [lindex $modfile 0] - set ModulesCurrentModulefile $modfile - - if {[info exists g_loadedModules($currentModule)]} { - pushMode "unload" - pushModuleName $currentModule - saveSettings - if {[execute-modulefile $modfile]} { - restoreSettings - } else { - unload-path LOADEDMODULES $currentModule $g_def_separator - unload-path _LMFILES_ $modfile $g_def_separator - unset g_loadedModules($currentModule) - if {[info exists g_loadedModulesGeneric([file dirname\ - $currentModule])]} { - unset g_loadedModulesGeneric([file dirname\ - $currentModule]) - } - } - - popMode - popModuleName - } - } else { - if {[info exists g_loadedModulesGeneric($mod)]} { - set mod "$mod/$g_loadedModulesGeneric($mod)" - } - unload-path LOADEDMODULES $mod $g_def_separator - unload-path _LMFILES_ $modfile $g_def_separator - if {[info exists g_loadedModules($mod)]} { - unset g_loadedModules($mod) - } - if {[info exists g_loadedModulesGeneric([file dirname $mod])]} { - unset g_loadedModulesGeneric([file dirname $mod]) - } - } - } errMsg ]} { - reportWarning "ERROR: module: module unload $mod failed.\n$errMsg" - } - } -} - - -proc cmdModulePurge {{separator {}}} { - global env g_def_separator g_debug - - if {$g_debug} { - report "DEBUG cmdModulePurge: $separator" - } - if {$separator == "" } { - set separator $g_def_separator - } - - if {[info exists env(LOADEDMODULES)]} { - set list [split $env(LOADEDMODULES) $separator] - eval cmdModuleUnload [reverseList $list] - } -} - - -proc cmdModuleReload {{separator {}}} { - global env g_def_separator g_debug - - if {$g_debug} { - report "DEBUG cmdModuleReload: $separator" - } - - if {$separator == "" } { - set separator $g_def_separator - } - - if {[info exists env(LOADEDMODULES)]} { - set list [split $env(LOADEDMODULES) $separator] - set rlist [reverseList $list] - foreach mod $rlist { - cmdModuleUnload $mod - } - foreach mod $list { - cmdModuleLoad $mod - } - } -} - -proc cmdModuleAliases {} { - - global DEF_COLUMNS g_moduleAlias g_moduleVersion g_debug - - set label "Aliases" - set len [string length $label] - set lrep [expr {($DEF_COLUMNS - $len - 2)/2}] - set rrep [expr {$DEF_COLUMNS - $len - 2 - $lrep}] - - report "[string repeat {-} $lrep] $label [string repeat {-} $rrep]" - - foreach name [lsort -dictionary [array names g_moduleAlias]] { - report "$name -> $g_moduleAlias($name)" - } - - set label "Versions" - set len [string length $label] - set lrep [expr {($DEF_COLUMNS - $len - 2)/2}] - set rrep [expr {$DEF_COLUMNS - $len - 2 - $lrep}] - - report "[string repeat {-} $lrep] $label [string repeat {-} $rrep]" - - foreach name [lsort -dictionary [array names g_moduleVersion]] { - report "$name -> $g_moduleVersion($name)" - } -} - -proc system {mycmd args} { - global g_systemList g_debug - - if {$g_debug} { - report "DEBUG system: $mycmd $args" - } - set mode [currentMode] - set mycmd [join [concat $mycmd $args] " "] - - if {$mode == "load"} { - lappend g_systemList $mycmd - }\ - elseif {$mode == "unload"} { - # No operation here unable to undo a syscall. - }\ - elseif {$mode == "display"} { - report "system\t\t$mycmd" - } - return {} -} - -proc cmdModuleAvail {{mod {*}}} { - global env ignoreDir DEF_COLUMNS flag_default_mf flag_default_dir - global show_oneperline show_modtimes g_def_separator - - if {$show_modtimes} { - report "- Package -----------------------------.- Versions -.- Last\ - mod. ------" - } - - foreach dir [split $env(MODULEPATH) $g_def_separator] { - if {[file isdirectory "$dir"] && [file readable $dir]} { - set len [string length $dir] - set lrep [expr {($DEF_COLUMNS - $len - 2)/2}] - set rrep [expr {$DEF_COLUMNS - $len - 2 - $lrep}] - report "[string repeat {-} $lrep] $dir [string repeat {-} $rrep]" - set list [listModules "$dir" "$mod" 0 "" $flag_default_mf\ - $flag_default_dir] - # sort names (sometimes? returned in the order as they were - # created on disk :-) - set list [lsort $list] - if {$show_modtimes} { - foreach i $list { - # don't change $i with the regsub - we need it - # to figure out the file time. - regsub {\(default\)} $i " (default)" i2 - set filetime [clock format [file mtime [lindex [getPathToModule $i] 0]] -format "%Y/%m/%b %H:%M:%S" ] - report [format "%-53s%10s" $i2 $filetime] - } - }\ - elseif {$show_oneperline} { - foreach i $list { - regsub {\(default\)} $i " (default)" i2 - report "$i2" - } - } else { - set max 0 - foreach mod2 $list { - if {[string length $mod2] > $max} { - set max [string length $mod2] - } - } - incr max 1 - set cols [expr {int($DEF_COLUMNS / $max)}] - # safety check to prevent divide by zero error below - if {$cols <= 0} { - set cols 1 - } - - # There is no '{}' at the begining of this 'list' as there is\ - in cmd - # ModuleList - ? - set item_cnt [expr {[llength $list] - 0}] - set rows [expr {int($item_cnt / $cols)}] - set lastrow_item_cnt [expr {int($item_cnt % $cols)}] - if {$lastrow_item_cnt > 0} { - incr rows - } - for {set row 0} {$row < $rows} {incr row} { - for {set col 0} {$col < $cols} {incr col} { - set index [expr {$col * $rows + $row}] - set mod2 [lindex $list $index] - if {$mod2 != ""} { - set mod2 [format "%-${max}s" $mod2] - report $mod2 -nonewline - } - } - report "" - } - } - } - } -} - -proc cmdModuleUse {args} { - global env g_debug g_def_separator g_debug - - if {$g_debug} { - report "DEBUG cmdModuleUse: $args" - } - - if {$args == ""} { - showModulePath - } else { - set stuff_path "prepend" - foreach path $args { - if {$path == ""} { - # Skip "holes" - }\ - elseif {($path == "--append") ||($path == "-a") ||($path ==\ - "-append")} { - set stuff_path "append" - }\ - elseif {($path == "--prepend") ||($path == "-p") ||($path ==\ - "-prepend")} { - set stuff_path "prepend" - }\ - elseif {[file isdirectory $path]} { - if {$g_debug} { - report "DEBUG cmdModuleUse: calling add-path \ - MODULEPATH $path $stuff_path $g_def_separator" - } - - pushMode "load" - catch { - add-path MODULEPATH $path $stuff_path $g_def_separator - } - popMode - } else { - report "+(0):WARN:0: Directory '$path' not found" - } - } - } -} - -proc cmdModuleUnuse {args} { - global g_def_separator g_debug - - if {$g_debug} { - report "DEBUG cmdModuleUnuse: $args" - } - if {$args == ""} { - showModulePath - } else { - global env - foreach path $args { - regsub -all {\/} $path {\/} escpath - set regexp [subst {(^|\:)${escpath}(\:|$)}] - if {[info exists env(MODULEPATH)] && [regexp $regexp\ - $env(MODULEPATH)]} { - - set oldMODULEPATH $env(MODULEPATH) - - if {$g_debug} { - report "calling unload-path MODULEPATH $path $g_def_separator" - } - - pushMode "unload" - catch { - unload-path MODULEPATH $path $g_def_separator - } - popMode - if {[info exists env(MODULEPATH)] && $oldMODULEPATH ==\ - $env(MODULEPATH)} { - reportWarning "WARNING: Did not unuse $path" - } - } - } - } -} - -proc cmdModuleDebug {{separator {}}} { - global env g_def_separator g_debug - - if {$g_debug} { - report "DEBUG cmdModuleDebug: $separator" - } - - if {$separator == "" } { - set separator $g_def_separator - } - - foreach var [array names env] { - array set countarr [getReferenceCountArray $var $separator] - - foreach path [array names countarr] { - report "$var\t$path\t$countarr($path)" - } - unset countarr - } - foreach dir [split $env(PATH) $separator] { - foreach file [glob -nocomplain -- "$dir/*"] { - if {[file executable $file]} { - set exec [file tail $file] - lappend execcount($exec) $file - } - } - } - foreach file [lsort -dictionary [array names execcount]] { - if {[llength $execcount($file)] > 1} { - report "$file:\t$execcount($file)" - } - } -} - -proc cmdModuleAutoinit {} { - global g_autoInit g_debug - - if {$g_debug} { - report "DEBUG cmdModuleAutoinit:" - } - set g_autoInit 1 -} - -proc cmdModuleInit {args} { - - global g_shell env g_debug - set moduleinit_cmd [lindex $args 0] - set notdone 1 - set notclear 1 - - if {$g_debug} { - report "DEBUG cmdModuleInit: $args" - } - - # Define startup files for each shell - set files(csh) [list ".modules" ".cshrc" ".cshrc_variables" ".login"] - set files(tcsh) [list ".modules" ".tcshrc" ".cshrc" ".cshrc_variables"\ - ".login"] - set files(sh) [list ".modules" ".bash_profile" ".bash_login" ".profile"\ - ".bashrc"] - set files(bash) $files(sh) - set files(ksh) $files(sh) - set files(zsh) [list ".modules" ".zshrc" ".zshenv" ".zlogin"] - - array set nargs { - list 0 - add 1 - load 1 - prepend 1 - rm 1 - unload 1 - switch 2 - clear 0 - } - - # Process startup files for this shell - set current_files $files($g_shell) - foreach filename $current_files { - if {$notdone && $notclear} { - set filepath $env(HOME) - append filepath "/" $filename - # create a new file to put the changes in - set newfilepath "$filepath-NEW" - - if {$g_debug} { - report "DEBUG Looking at: $filepath" - } - if {[file readable $filepath] && [file isfile $filepath]} { - set fid [open $filepath r] - - set temp [expr {[llength $args] -1}] - if {$temp != $nargs($moduleinit_cmd)} { - error "'module init$moduleinit_cmd' requires exactly\ - $nargs($moduleinit_cmd) arg(s)." - # cmdModuleHelp - exit -1 - } - - # Only open the new file if we are not doing "initlist" - if {[string compare $moduleinit_cmd "list"] != 0} { - set newfid [open $newfilepath w] - } - - while {[gets $fid curline] >= 0} { - # Find module load/add command in startup file - set comments {} - if {$notdone && [regexp {^([ \t]*module[ \t]+(load|add)[\ - \t]+)(.*)} $curline match cmd subcmd modules]} { - regexp {([ \t]*\#.+)} $modules match comments - regsub {\#.+} $modules {} modules - # remove existing references to the named module from\ - the list - # Change the module command line to reflect the given\ - command - switch $moduleinit_cmd { - list { - report "$g_shell initialization file $filepath\ - loads modules: $modules" - } - add { - set newmodule [lindex $args 1] - set modules [replaceFromList $modules $newmodule] - append modules " $newmodule" - puts $newfid "$cmd$modules$comments" - set notdone 0 - } - prepend { - set newmodule [lindex $args 1] - set modules [replaceFromList $modules $newmodule] - set modules "$newmodule $modules" - puts $newfid "$cmd$modules$comments" - set notdone 0 - } - rm { - set oldmodule [lindex $args 1] - set modules [replaceFromList $modules $oldmodule] - if {[llength $modules] == 0} { - set modules "" - } - puts $newfid "$cmd$modules$comments" - set notdone 0 - } - switch { - set oldmodule [lindex $args 1] - set newmodule [lindex $args 2] - set modules [replaceFromList $modules\ - $oldmodule $newmodule] - puts $newfid "$cmd$modules$comments" - set notdone 0 - } - clear { - set modules "" - puts $newfid "$cmd$modules$comments" - set notclear 0 - } - default { - report "Command init$moduleinit_cmd not\ - recognized" - } - } - } else { - # copy the line from the old file to the new - if {[info exists newfid]} { - puts $newfid $curline - } - } - } - close $fid - if {[info exists newfid]} { - close $newfid - if {[catch {file copy -force $filepath $filepath-OLD}] !=\ - 0} { - report "Failed to back up original $filepath...exiting" - exit -1 - } - if {[catch {file copy -force $newfilepath $filepath}] !=\ - 0} { - report "Failed to write $filepath...exiting" - exit -1 - } - } - } - } - } -} - -proc cmdModuleHelp {args} { - global done MODULES_CURRENT_VERSION - - set done 0 - foreach arg $args { - if {$arg != ""} { - set modfile [getPathToModule $arg] - - if {$modfile != ""} { - pushModuleName [lindex $modfile 1] - set modfile [lindex $modfile 0] - report\ - "-------------------------------------------------------------------" - report "Module Specific Help for $modfile:\n" - set mode "Help" - execute-modulefile $modfile 1 - popMode - popModuleName - report\ - "-------------------------------------------------------------------" - } - set done 1 - } - } - if {$done == 0} { - report "Modules Release Tcl $MODULES_CURRENT_VERSION " 1 - report { Copyright GNU GPL v2 1991} - report {Usage: module [ command ]} - - report {Commands:} - report { list [switches] modulefile\ - [modulefile ...]} - report { display | show modulefile\ - [modulefile ...]} - report { add | load modulefile\ - [modulefile ...]} - report { purge | rm | unload modulefile\ - [modulefile ...]} - report { reload modulefile\ - [modulefile ...]} - report { switch | swap \ - [oldmodulefile] newmodulefile} - report { avail [switches] [modulefile\ - [modulefile ...]]} - report { aliases} - report { whatis [modulefile\ - [modulefile ...]]} - report { help [modulefile\ - [modulefile ...]]} - report { path modulefile} - report { paths modulefile} - report { initlist modulefile} - report { initadd modulefile} - report { initrm modulefile} - report { initclear modulefile} - report { initprepend modulefile} - report { use dir [dir ...]} - report { unuse dir [dir ...]} - report { source scriptfile} - report { apropos | keyword | search string} - report {Switches:} - report { -t terse format avail and list} - report { -l long format avail and list} - } -} - - -######################################################################## -# main program - -# needed on a gentoo system. Shouldn't hurt since it is -# supposed to be the default behavior -fconfigure stderr -translation auto - -if {$g_debug} { - report "CALLING $argv0 $argv" -} - -# Parse options -set opt [lindex $argv 1] -switch -regexp -- $opt { - {^(-deb|--deb)} { - - if {!$g_debug} { - report "CALLING $argv0 $argv" - } - - set g_debug 1 - report "DEBUG debug enabled" - - set argv [replaceFromList $argv $opt] - } - {^(--help|-h)} { - cmdModuleHelp - exit 0 - } - {^(-V|--ver)} { - report "Modules Release Tcl $MODULES_CURRENT_VERSION" - exit 0 - } - {^--} { - report "+(0):ERROR:0: Unrecognized option '$opt'" - exit -1 - } -} - -set g_shell [lindex $argv 0] -set command [lindex $argv 1] -set argv [lreplace $argv 0 1] - -switch -regexp -- $g_shell { - ^(sh|bash|ksh|zsh)$ { - set g_shellType sh - } - ^(cmd)$ { - set g_shellType cmd - } - ^(csh|tcsh)$ { - set g_shellType csh - } - ^(perl)$ { - set g_shellType perl - } - ^(python)$ { - set g_shellType python - } - ^(lisp)$ { - set g_shellType lisp - } - . { - error " +(0):ERROR:0: Unknown shell type \'($g_shell)\'" - } -} - -cacheCurrentModules - -# Find and execute any .modulerc file found in the module directories defined\ - in env(MODULESPATH) -runModulerc -# Resolve any aliased module names - safe to run nonmodule arguments -if {$g_debug} { - report "DEBUG Resolving $argv" -} - -if {[lsearch $argv "-t"] >= 0} { - set show_oneperline 1 - set argv [replaceFromList $argv "-t"] -} -if {[lsearch $argv "-l"] >= 0} { - set show_modtimes 1 - set argv [replaceFromList $argv "-l"] -} -set argv [resolveModuleVersionOrAlias $argv] -if {$g_debug} { - report "DEBUG Resolved $argv" -} - -if {[catch { - switch -regexp -- $command { - {^av} { - if {$argv != ""} { - foreach arg $argv { - cmdModuleAvail $arg - } - } else { - cmdModuleAvail - cmdModuleAliases - } - } - {^al} { - cmdModuleAliases - } - {^li} { - cmdModuleList - } - {^(di|show)} { - foreach arg $argv { - cmdModuleDisplay $arg - } - } - {^(add|lo)} { - eval cmdModuleLoad $argv - renderSettings - } - {^source} { - eval cmdModuleSource $argv - renderSettings - } - {^paths} { - # HMS: We probably don't need the eval - eval cmdModulePaths $argv - renderSettings - } - {^path} { - # HMS: We probably don't need the eval - eval cmdModulePath $argv - renderSettings - } - {^pu} { - cmdModulePurge - renderSettings - } - {^sw} { - eval cmdModuleSwitch $argv - renderSettings - } - {^(rm|unlo)} { - eval cmdModuleUnload $argv - renderSettings - } - {^use$} { - eval cmdModuleUse $argv - renderSettings - } - {^unuse$} { - eval cmdModuleUnuse $argv - renderSettings - } - {^wh} { - if {$argv != ""} { - foreach arg $argv { - cmdModuleWhatIs $arg - } - } else { - cmdModuleWhatIs - } - } - {^(apropos|search|keyword)$} { - eval cmdModuleApropos $argv - } - {^debug$} { - eval cmdModuleDebug - } - {^rel} { - cmdModuleReload - renderSettings - } - {^init(add|lo)$} { - eval cmdModuleInit add $argv - } - {^initprepend$} { - eval cmdModuleInit prepend $argv - } - {^initswitch$} { - eval cmdModuleInit switch $argv - } - {^init(rm|unlo)$} { - eval cmdModuleInit rm $argv - } - {^initlist$} { - eval cmdModuleInit list $argv - } - {^initclear$} { - eval cmdModuleInit clear $argv - } - {^autoinit$} { - cmdModuleAutoinit - renderSettings - } - {^($|help)} { - cmdModuleHelp $argv - } - . { - reportWarning "ERROR: command '$command' not recognized" - cmdModuleHelp $argv - } - } -} errMsg ]} { - reportWarning "ERROR: $errMsg" -} - -# ;;; Local Variables: *** -# ;;; mode:tcl *** -# ;;; End: *** diff --git a/Pmodules/modulefile b/Pmodules/modulefile index 1893fdb..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_ROOT}/Tools/Pmodules/VERSION' 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/profile.bash.in b/Pmodules/profile.bash.in index f25eee8..985ce2f 100644 --- a/Pmodules/profile.bash.in +++ b/Pmodules/profile.bash.in @@ -1,13 +1,15 @@ #!/bin/bash # -# The following settings are system defaults. They can be (re-)defined -# in a system wide profile or in a user's profile. # set default version : ${PMODULES_VERSION:=@PMODULES_VERSION@} +export PMODULES_VERSION -declare -x PMODULES_ROOT=$(cd $(dirname "${BASH_SOURCE}")/.. && pwd) -declare -x PMODULES_HOME="${PMODULES_ROOT}/Tools/Pmodules/${PMODULES_VERSION}" +##### no changes below this line ###### + +declare __pm_root__=$(cd $(dirname "${BASH_SOURCE}")/.. && pwd) +declare -x PMODULES_HOME="${__pm_root__}/Tools/Pmodules/${PMODULES_VERSION}" +unset __pm_root__ test -r "${PMODULES_HOME}/init/bash" && source "$_" diff --git a/Pmodules/profile.csh.in b/Pmodules/profile.csh.in index b00013e..4409628 100755 --- a/Pmodules/profile.csh.in +++ b/Pmodules/profile.csh.in @@ -9,28 +9,8 @@ if ( ${PMODULES_VERSION} == "" ) then setenv PMODULES_VERSION "@PMODULES_VERSION@" endif -############################################################################# -# N O C H A N G E S B E L O W T H I S L I N E ! # -############################################################################# +setenv PMODULES_HOME "@PMODULES_ROOT@/Tools/Pmodules/${PMODULES_VERSION}" -setenv PMODULES_MODULEFILES_DIR 'modulefiles' -setenv ReleaseStages ':unstable:stable:deprecated:' - -setenv PMODULES_ROOT "@PMODULES_ROOT@" -setenv PMODULES_CONFIG_DIR 'config' -setenv PMODULES_HOME "${PMODULES_ROOT}/Tools/Pmodules/${PMODULES_VERSION}" -setenv PMODULES_DIR "${PMODULES_HOME}" - -############################################################################ -# some sanity checks -# - -# fixme: add some checks - - -############################################################################ -# inititialize Pmodules for csh -# set _init_csh="${PMODULES_HOME}/init/csh" if ( ! -r "${_init_csh}" ) then echo "Oops: cannot initialize Modules!" diff --git a/Pmodules/profile.zsh.in b/Pmodules/profile.zsh.in index 8077e79..6accc21 100644 --- a/Pmodules/profile.zsh.in +++ b/Pmodules/profile.zsh.in @@ -1,50 +1,20 @@ -#!/bin/bash -# -# Notes: -# - PMODULES_ROOT is derived from the location of this file. -# - Some for PMODULES_CONFIG_DIR. -# - The Pmodules software must be installed in -# ${PMODULES_ROOT}/Tools/Pmodules/${PMODULES_VERSION} +#!/bin/zsh # -declare -xa DefaultGroups -declare -xa DefaultReleaseStages -declare -x PMODULES_VERSION - -: ${DefaultGroups:=(Tools Programming)} -: ${DefaultReleaseStages:=(stable)} +# set default version : ${PMODULES_VERSION:=@PMODULES_VERSION@} -############################################################################# -# N O C H A N G E S B E L O W T H I S L I N E ! # -############################################################################# +##### no changes below this line ###### +export PMODULES_VERSION -declare -x PMODULES_MODULEFILES_DIR='modulefiles' -declare -x ReleaseStages=':unstable:stable:deprecated:' +declare __this_file__=${(%):-%N} +declare -x PMODULES_HOME="${__this_file____:h:h}/Tools/Pmodules/${PMODULES_VERSION}" +unset __this_file__ -declare -x PMODULES_ROOT=$(cd $(dirname "${(%):-%N}")/.. && pwd) -declare -x PMODULES_CONFIG_DIR=$(basename $(cd $(dirname "${(%):-%N}") && pwd)) -declare -x PMODULES_HOME="${PMODULES_ROOT}/Tools/Pmodules/${PMODULES_VERSION}" -declare -x PMODULES_DIR="${PMODULES_HOME}" - -############################################################################ -# some sanity checks -# -if [[ ! -d ${PMODULES_ROOT} ]]; then - echo "Oops: ${PMODULES_ROOT}: Set as Pmodules root, but this is not a directory." 1>&2 - return 1 -fi -if [[ ! -d ${PMODULES_HOME} ]]; then - echo "Oops: ${PMODULES_HOME}: Set as Pmodules home, but this is not a directory." 1>&2 - return 1 -fi - -############################################################################ -# inititialize Pmodules for zsh -# test -r "${PMODULES_HOME}/init/zsh" && source "$_" -if (( $? != 0 )); then - echo "Oops: cannot initialize Pmodules environment!" + +if [[ $? != 0 ]]; then + echo "Oops: cannot initialize Pmodules!" return 1 fi diff --git a/Pmodules/yq.x86_64_Darwin b/Pmodules/yq.x86_64_Darwin new file mode 100755 index 0000000..12069b6 Binary files /dev/null and b/Pmodules/yq.x86_64_Darwin differ diff --git a/Pmodules/yq.x86_64_Linux b/Pmodules/yq.x86_64_Linux new file mode 100755 index 0000000..9e2a58f Binary files /dev/null and b/Pmodules/yq.x86_64_Linux differ diff --git a/Pmodules/zsh b/Pmodules/zsh index f1a0a4c..b6b800b 100644 --- a/Pmodules/zsh +++ b/Pmodules/zsh @@ -1,105 +1,19 @@ #!/bin/zsh -############################################################################# -# implement module comand as function +############################################################################ +# some sanity checks # -module() { - local -r modulecmd="${PMODULES_HOME}/bin/modulecmd" - - local -a args=() - local -a modulecmd_opts=() - local -a subcmd_opts=() - - while (( $# > 0 )); do - case $1 in - -* ) - modulecmd_opts+=( $1 ) - shift - ;; - * ) - break - ;; - esac - done - local -r subcmd="$1" - shift - while (( $# > 0 )); do - case $1 in - -* ) - subcmd_opts+=( $1 ) - ;; - [/~a-zA-Z]* ) - args+=( $1 ) - ;; - esac - shift - done - - [[ -z ${subcmd} ]] && args+=( 'help' ) - [[ ${#args[@]} == 0 ]] && args+=( '--' ) - - # Loop over all modules to load. - # Note: We have to eval here, otherwise we cannot do something like - # $ module load gcc/5.2.0 openmpi/1.8.8 hdf5/1.8.15 - local m - for module in ${args[@]}; do - eval $( "${modulecmd}" zsh "${modulecmd_opts[@]}" "${subcmd}" \ - "${subcmd_opts[@]}" "${module}" ) - done -} -export -f module +if [[ ! -d ${PMODULES_HOME} ]]; then + echo "Oops: ${PMODULES_HOME}: Set as Pmodules home, but this is not a directory." 1>&2 + return 1 +fi ############################################################################# -# helper functions +# initialize zsh completion # -save_env() { - local s='' - while (( $# > 0 )); do - s+="$( typeset -p $1 );" - shift - done - echo export PMODULES_ENV=$( "${PMODULES_HOME}/sbin/base64" --wrap=0 <<< "$s" ) -} - -############################################################################# -# setup environment -# -declare -x LOADEDMODULES='' -declare -x _LMFILES_='' - -# build initial MODULEPATH -declare -x MODULEPATH='' -typeset -T MODULEPATH modulepath -for group in ${DefaultGroups[@]}; do - dir="${PMODULES_ROOT}/${group}/${PMODULES_MODULEFILES_DIR}" - modulepath=( "${dir}" ${(m)modulepath:#${dir}} ) -done - -# build initial list of used releases -declare -x UsedReleases='' -typeset -T UsedReleases usedreleases -for r in ${DefaultReleaseStages[@]}; do - usedreleases=( "${r}" ${(m)usedreleases:#${r}} ) -done - -eval $(save_env UsedReleases DefaultReleaseStages DefaultGroups ReleaseStages) - -unset UsedReleases -unset DefaultReleaseStages -unset DefaultGroups -unset ReleaseStages - -# initialize MANPATH with output of `man --path` if not set -[[ -z "${MANPATH}" ]] && manpath=$( man --path ) - -# add man pages of Pmodules and remove all other Pmodules man pages -manpath=( "${PMODULES_HOME}/share/man" ${(m)manpath:#${dirs_to_remove}} ) - -# initialize completion -test -r "${PMODULES_HOME}/init/zsh_completion" && source "$_" - -# cleanup -unset group dir r dirs_to_remove +#if [[ -r "${PMODULES_HOME}/init/bash_completion" ]]; then +# source "${PMODULES_HOME}/init/bash_completion" +#fi ############################################################################# # legacy... @@ -107,6 +21,26 @@ unset group dir r dirs_to_remove unset MODULE_VERSION unset MODULE_VERSION_STACK unset MODULESHOME +unset PMODULES_ENV + +declare -x PMODULES_DIR="${PMODULES_HOME}" + + +############################################################################# +# implement module comand as shell function +# +module() { + eval $("${PMODULES_HOME}/bin/modulecmd" bash "$@") +} +export -f module + +modbuild(){ + ( + eval $("${PMODULES_HOME}/bin/modulecmd" bash load System:bash) + "${PMODULES_HOME}/bin/modbuild" "$@" + ) +} +export -f modbuild # Local Variables: # mode: sh diff --git a/Tools/Tcl/build b/Tools/Tcl/build deleted file mode 100755 index 47e53a8..0000000 --- a/Tools/Tcl/build +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env modbuild - -pbuild::set_download_url "ftp://ftp.tcl.tk/pub/tcl/tcl8_6/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 100644 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 8a1f45f..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_ROOT}/Tools/Pmodules/${PMODULES_VERSION}" - -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_ROOT}/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 100644 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 560b7c8..99f8959 100755 --- a/build +++ b/build @@ -1,48 +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 -r OS=$(uname -s) +set -o nounset +set -o pipefail +shopt -s nullglob -declare BOOTSTRAP_DIR=$(dirname "$0") -source "${BOOTSTRAP_DIR}/Pmodules/libstd.bash" || { echo "Oops!" 1>&2; exit 42; } - -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" -# these variables need to be defined but must be initialized with an empty string -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='modbuild.conf' - -# defaults -declare -rx DEFAULT_PMODULES_ROOT='/opt/psi' -declare -rx DEFAULT_DISTFILES_DIR="var/distfiles" -declare -rx DEFAULT_VERSIONS_CONFIG="${CONFIG_DIR}/versions.conf" -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() { - local -r name="$1" - echo $(awk "/^$1[[:blank:]]/ {print \$2}" "${DEFAULT_VERSIONS_CONFIG}") +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}}" } -declare -rx PMODULES_VERSION=$(get_version 'Pmodules') +#----------------------------------------------------------------------------- +# The next functions are used in the sub-commands, if an illegal option +# or argument has been passed. +# +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 @@ -65,59 +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' # -read_config_file() { - local var="$1" - local fname="${!var}" - if [[ ! -r "${fname}" ]]; then - std::die 1 "Configuration file '${fname}' does not exist or is not readable!" - fi - - source "${fname}" || std::die 1 "Cannot read configuration file '${fname}'" - - if [[ -z "${PMODULES_ROOT}" ]]; then - std::die 1 "Error in configuration file '${fname}': PMODULE_ROOT not defined!" - fi - if [[ -z "${PMODULES_VERSION}" ]]; then - std::die 1 "Error in configuration file '${fname}': PMODULE_VERSION not defined!" - fi - if [[ -z "${PMODULES_DISTFILESDIR}" ]]; then - std::die 1 "Error in configuration file '${fname}': PMODULES_DISTFILESDIR not defined!" - fi - if [[ -z "${PMODULES_TMPDIR}" ]]; then - std::die 1 "Error in configuration file '${fname}': PMODULE_TMPDIR not defined!" - fi - - # we have to make these variables global, otherwise they are not set - # in the calling function - declare -g PMODULES_ROOT="${PMODULES_ROOT}" - declare -g PMODULES_HOME="${PMODULES_HOME}" - declare -g PMODULES_DISTFILESDIR="${PMODULES_DISTFILESDIR}" - declare -g PMODULES_TMPDIR="${PMODULES_TMPDIR}" - - std::upvar "${var}" "${fname}" -} - -#----------------------------------------------------------------------------- -# -pmodules::help_configure() { +build::help_configure() { echo " Usage: $(basename $0) configure [OPTION...] @@ -125,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 '${DEFAULT_PMODULES_DISTFILESDIR}' 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 '${DEFAULT_PMODULES_TMPDIR}' + The default is '${DEFAULT_INSTALL_ROOT}/${DEFAULT_TMP_DIR}' ---help +--force|-f + Override existing configuration. + +--help|-h|-? Print this help text. " 1>&2 @@ -147,102 +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:@PMODULES_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..." - 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" + #--- + # 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...] @@ -250,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. @@ -273,266 +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}" \ - "--enable-cleanup" \ - "--force-rebuild" \ - "--debug" \ - "--verbose" \ - "${BOOTSTRAP_DIR}/Tools/${name}/build" \ - "${build_opts[@]}" "$@" "${version}" || \ - std::die 3 "Compiling '${name}' failed!" - } - - 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 [[ ! -f "${PMODULES_HOME}/${UTILBIN_DIR}/base64" ]] || [[ ${opt_force} == 'yes' ]]; then - # build coreutils - #fi + install -m 0755 -d "${PREFIX}"/{bin,init,lib,libexec} \ - if [[ "${OS}" == 'Darwin' ]]; then - if [[ ! -f "${PMODULES_HOME}/${UTILBIN_DIR}/getopt" ]] || [[ ${opt_force} == 'yes' ]]; then - build getopt - fi - - 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() { - local prefix="${PMODULES_ROOT:-${DEFAULT_PMODULES_ROOT}}" - 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: ${PMODULES_ROOT}" - 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" > "${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.conf" || \ - install -m 0644 "${SRC_DIR}/Pmodules.conf" "${PMODULES_ROOT}/${CONFIG_DIR}" - - 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 0755 -d "${PMODULES_ROOT}/Tools/modulefiles/Pmodules" - install -m 0644 "${SRC_DIR}/modulefile" "${PMODULES_ROOT}/Tools/modulefiles/Pmodules/${PMODULES_VERSION}" - mkdir -p "${PMODULES_ROOT}/Libraries/modulefiles" - + 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=() @@ -552,6 +523,7 @@ while (( $# > 0 )); do shift 1 subcmd_args=( "$@" ) shift $# + break ;; * ) std::die 1 "Invalid sub-command '$1'.\n\nUse 'build --help' to get help..." @@ -562,5 +534,10 @@ 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 +# sh-basic-offset: 8 +# tab-width: 8 +# End: diff --git a/config/Pmodules.yaml.in b/config/Pmodules.yaml.in new file mode 100644 index 0000000..5db06a2 --- /dev/null +++ b/config/Pmodules.yaml.in @@ -0,0 +1,9 @@ +DefaultGroups: Tools:Programming +DefaultReleaseStages: stable +ReleaseStages: unstable:stable:deprecated +TmpDir: @PMODULES_TMPDIR@ +DistfilesDir: @PMODULES_DISTFILESDIR@ + +Overlays: + base: + install_root: @INSTALL_ROOT@ diff --git a/config/modbuild.conf.in b/config/modbuild.conf.in deleted file mode 100644 index cfce2aa..0000000 --- a/config/modbuild.conf.in +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -# -: ${PMODULES_ROOT:=@PMODULES_ROOT@} -: ${PMODULES_DISTFILESDIR:=@PMODULES_DISTFILESDIR@} -: ${PMODULES_TMPDIR:=@PMODULES_TMPDIR@} -export PMODULES_ROOT -export PMODULES_DISTFILESDIR -export PMODULES_TMPDIR - -declare -x PMODULES_CONFIG_DIR='config' -declare -x PMODULES_MODULEFILES_DIR='modulefiles' - -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/doc/Overlays.adoc b/doc/Overlays.adoc new file mode 100644 index 0000000..ea725be --- /dev/null +++ b/doc/Overlays.adoc @@ -0,0 +1,276 @@ += 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 + +Information is in the YAML configuration file: + +==== General structure + +name:: The name of the Pmodule without version. Example: `hdf5` +group:: group the module is in. Example: `MPI` +overlay:: (optional) overlay, defaults to `base` if not specified +relstage:: (optional) default release stage. If not defined, + it defaults to `unstable`, +url:: (optional) download URL, Must be either define here or per + version. The environment variables `$P`, `$V`, `$V_PKG` etc. can be used. The downloaded file is stored with the file name `$P-${V_PKG}.ext`. Whereby `ext` is derived from the URL. +addon_sources:: (optional) array of sources for add-ons. Example: `git-lfs` +use:: (optional) use flags. +systems:: (optional) array of supported systems, defaults to any. Example: `[rhel6 rhel7]`. +compilers:: (optional) array pf supported compilers, defaults to any. Example: `[gcc intel]`. +compile-in-src:: (optional) compile in source tree, allowed vaules are `yes` and `no`, the default is `no`. +configure-with:: (optional) If a software supports CMake and autotools, this specifies the tool to use. Allowed values are `autotools`, `cmake`, `auto`, `none`. The default is `auto`. +versions:: array of `version` objects + +==== Objects of type `addon_source` + +name:: unique name +url:: download URL. +shasum:: (optional) SHA256 sum. + +==== Objects of type `version` + +dependencies:: other run-time dependencies, if any +build_with:: dependencies required for building, if any +overlay:: (optional) override the default. Example: `devel`. +relstage:: (optional) override the default. Example: `unstable`. +addon_sources:: (optional) array of sources for add-ons. +systems:: (optional) override the default +compilers:: (optional) override the default +compile-in-src:: (optional) override the default +configure-with:: (optional) override the default +variants:: array of `variant` objects + +==== Object of type `variant` +with:: hierarchical dependencies, `with` must be specified only +for hierarchical groups. +dependencies:: other run-time dependencies, if any +build_requires:: dependencies required for building, if any +addon_sources:: (optional) array of sources for add-ons. +use:: (optional) use flags +overlay:: (optional) override the default. Example: `devel`. +relstage:: (optional) override the default. Example: `unstable`. +systems:: (optional) override the default +compilers:: (optional) override the default + +==== Example of a configuration files in YAML format + +.YAML configuration file for serial HDF5 +==== +.... +name: hdf5 +group: MPI +overlay: base +relstage: stable +url: https://support.hdfgroup.org/ftp/HDF5/releases/$P-${V_MAJOR}.${V_MINOR}/$P-${V_PKG}/src/$P-${V_PKG}.tar.gz +systems: [rhel6,rhel7,rhel8] +compilers: [gcc,intel,pgi] +compile_in_sourcetree: no +configure_with: auto + +versions: + 1.12.2: + shasum: 3016ea56a175d2ca7f2568c8016420f7a2aad8f95e214fe7fa5485f4b80fbe51 + variants: + - group_deps: + compiler: + gcc: [5.5.0,6.5.0,7.5.0,10.2.0,10.3.0] + intel: .... + mpi: + openmpi: [...] + #hdf5: + runtime_deps: + cuda:10.0 + ... + build_deps: + name:version + + + + - with: + - gcc: [8.5.0,9.5.0,11.3.0,12.1.0] + relstage: unstable + overlay: devel +.... +==== + +.YAML configuration file for HDF5 (serial and parallel) +==== +.... +name: hdf5 +overlay: base +relstage: stable +url: https://support.hdfgroup.org/ftp/HDF5/releases/$P-${V_MAJOR}.${V_MINOR}/$P-${V_PKG}/src/$P-${V_PKG}.tar.gz +systems: [rhel6 rhel7 rhel8] +compilers: [gcc intel pgi] +compile_in_sourcetree: no +configure_with: auto + +versions: + 1.12.2: + shasum: 3016ea56a175d2ca7f2568c8016420f7a2aad8f95e214fe7fa5485f4b80fbe51 + variants: + - with: + - gcc [5.5.0 6.5.0 7.5.0 10.2.0 10.3.0] + group: Compiler + - with: + - gcc [8.5.0,9.5.0,11.3.0,12.1.0] + - openmpi [4.0.5-2] + group: MPI + relstage: unstable + overlay: devel +.... +==== + +.YAML configuration file for Git with git-lfs +==== +.... +name: git +defaults: + group: Tools + overlay: base + relstage: stable + url: https://mirrors.edge.kernel.org/pub/software/scm/$P/$P-${V_PKG}.tar.xz + systems: [rhel6 rhel7 rhel8] + compilers: [gcc] + compile_in_sourcetree: yes + configure_with: auto + script: build + +addons: + git-lfs/3.2.0: + - url: https://github.com/git-lfs/git-lfs/archive/refs/tags/v3.2.0.tar.gz + shasum: f8e6bbe043b97db8a5c16da7289e149a3fed9f4d4f11cffcc6e517c7870cd9e5 + build_requires: [go/1.19] + build_script: build-git-lfs + +versions: + 2.37.2: + shasum: 4c428908e3a2dca4174df6ef49acc995a4fdb1b45205a2c79794487a33bc06e5 + variants: + - dependencies: TclTk/8.6.9 + build_requires: [perl/5.30.0 asciidoc/8.6.9-1 xmlto/0.0.28 gettext/0.19.8] + relstage: unstable + overlay: devel + addons: + git-lfs: 3.2.0 + +.... +==== + +==== Format specification +.YAML format +.... +name: +source_url: "https://..." +overlay: +group: +relstage: +systems: [opt-array-of-supported] +compilers: [opt-array-of-supported-compilers] +compile-in-src: yes|no +configure-with: cmake|autotools|auto|none + +versions: + - : + overlay: + group: + relstage: + systems: [supported-systems] + sources: + - url: + shasum: + compilers: + compile-in-src: yes|no + configure-with: cmake|autotools|auto|none + variants: + - with: + - gcc: [5.5.0,6.5.0,7.5.0,10.2.0,10.3.0] + dependencies: [array-of-runtime-deps] + build_requires: [array-of-build-deps] + relstage: + use: [array-of-use-flags] + overlay: + ... +.... +==== 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: