Files
Pmodules/Pmodules/libpbuild.bash
T

1280 lines
35 KiB
Bash

#!/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
declare -A SOURCE_UNPACK_DIRS
#.............................................................................
#
# Exit script on errror.
#
# $1 exit code
#
set -o errexit
error_handler() {
local -i ec=$?
std::die ${ec} "Oops"
}
trap "error_handler" ERR
declare configure_with='undef'
#..............................................................................
# global variables
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=3
pbuild.jobs() {
JOBS="$1"
}
declare system=''
pbuild.system() {
system="$1"
}
declare TEMP_DIR=''
pbuild.temp_dir() {
TEMP_DIR="$1"
}
declare PMODULES_DISTFILESDIR=''
pbuild.pmodules_distfilesdir() {
PMODULES_DISTFILESDIR="$1"
}
declare verbose=''
pbuild.verbose() {
verbose="$1"
}
# 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)
declare -x fully_qualified_module_name=''
# 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'
#..............................................................................
#
# 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)
##############################################################################
#
# 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 (as printed by 'uname -s')
#
pbuild::supported_systems() {
SUPPORTED_SYSTEMS+=( "$@" )
}
#......................................................................
#
# 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
# fully_qualified_module_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."
# build module name
# :FIXME: this should be read from a configuration file
local name=()
case ${GROUP} in
Compiler )
name+=( "${COMPILER}/${COMPILER_VERSION}" )
name+=( "${module_name}/${module_version}" )
;;
MPI )
name+=( "${COMPILER}/${COMPILER_VERSION}" )
name+=( "${MPI}/${MPI_VERSION}" )
name+=( "${module_name}/${module_version}" )
;;
HDF5 )
name+=( "${COMPILER}/${COMPILER_VERSION}" )
name+=( "${MPI}/${MPI_VERSION}" )
name+=( "${HDF5}/${HDF5_VERSION}" )
name+=( "${module_name}/${module_version}" )
;;
OPAL )
name+=( "${COMPILER}/${COMPILER_VERSION}" )
name+=( "${MPI}/${MPI_VERSION}" )
name+=( "${OPAL}/${OPAL_VERSION}" )
name+=( "${module_name}/${module_version}" )
;;
HDF5_serial )
name+=( "${COMPILER}/${COMPILER_VERSION}" )
name+=( "hdf5_serial/${HDF5_SERIAL_VERSION}" )
name+=( "${module_name}/${module_version}" )
;;
* )
name+=("${module_name}/${module_version}" )
;;
esac
# set full module name
fully_qualified_module_name=$( join_by '/' "${name[@]}" )
# set PREFIX of module
PREFIX="${PMODULES_ROOT}/${GROUP}"
local -i i=0
for ((i=${#name[@]}-1; i >= 0; i--)); do
PREFIX+="/${name[i]}"
done
}
##############################################################################
#
# Install module in given group.
#
# Arguments:
# $1: group
#
pbuild::add_to_group() {
if [[ -z ${1} ]]; then
std::die 42 \
"%s " "${module_name}/${module_version}:" \
"${FUNCNAME}: missing group argument."
fi
GROUP="$1"
set_full_module_name_and_prefix
}
##############################################################################
#
# Set documentation file to be installed.
#
# 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) )
[[ "${output[0]}" == "$1" ]] && std::upvar "${uvar}" "${output[1]}"
}
pbuild::set_download_url() {
local -i _i=${#SOURCE_URLS[@]}
SOURCE_URLS[_i]="$1"
SOURCE_NAMES[_i]="$2"
}
pbuild::set_sha256sum() {
SOURCE_SHA256_SUMS+=("$1")
}
pbuild::set_unpack_dir() {
SOURCE_UNPACK_DIRS[$1]=$2
}
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::pre_prep() {
:
}
pbuild::post_prep() {
:
}
###############################################################################
#
# extract sources. For the time being only tar-files are supported.
#
pbuild::prep() {
#......................................................................
#
# Find/download tarball for given module.
#
# Arguments:
# $1: store file name with upvar here
# $2: download URL
# $3: output filename (can be empty string)
# $4...: download directories
#
# 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 \
--output "${output}" \
"${url}"
fi
}
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}"
}
download_source_file() {
local "$1"
local var="$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
[[ -r "${dir}/${fname}" ]] && break
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
fi
local sha256_sum=''
local hash=''
for hash in "${SOURCE_SHA256_SUMS[@]}"; do
if [[ ${hash} =~ $fname: ]]; then
sha256_sum="${hash#*:}"
fi
done
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}}"
(
if [[ -n "${dir}" ]]; then
mkdir -p "${dir}"
cd "${dir}"
fi
tar -xv --strip-components 1 -f "${file}"
)
}
patch_sources() {
cd "${SRC_DIR}"
local i=0
for ((_i = 0; _i < ${#PATCH_FILES[@]}; _i++)); do
std::info \
"%s %s\n" \
"${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]}"
done
}
[[ -z "${SOURCE_URLS}" ]] && \
std::die 3 \
"%s " "${module_name}/${module_version}:" \
"Download source not set!"
mkdir -p "${PMODULES_DISTFILESDIR}"
local i=0
local source_fname
for ((i = 0; i < ${#SOURCE_URLS[@]}; i++)); do
download_source_file \
source_fname \
"${SOURCE_URLS[i]}" \
"${SOURCE_NAMES[i]}" \
"${PMODULES_DISTFILESDIR}" \
"${BUILDBLOCK_DIR}" ||
std::die 4 \
"%s " "${module_name}/${module_version}:" \
"sources for not found."
unpack "${source_fname}" "${SOURCE_UNPACK_DIRS[${source_fname##*/}]}"
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}:" ]]
}
###############################################################################
#
#
pbuild::pre_configure() {
:
}
pbuild::set_configure_args() {
CONFIGURE_ARGS+=( "$@" )
}
pbuild::add_configure_args() {
CONFIGURE_ARGS+=( "$@" )
}
pbuild::use_autotools() {
configure_with='autotools'
}
pbuild::use_cmake() {
configure_with='cmake'
}
pbuild::configure() {
case "${configure_with}" in
autotools )
if [[ ! -r "${SRC_DIR}/configure" ]]; then
std::die 3 \
"%s " "${module_name}/${module_version}:" \
"${FNCNAME[0]}:" \
"autotools configuration not available, aborting..."
fi
;;
cmake )
if [[ ! -r "${SRC_DIR}/CMakeLists.txt" ]]; then
std::die 3 \
"%s " "${module_name}/${module_version}:" \
"${FNCNAME[0]}:" \
"CMake script not available, aborting..."
fi
;;
esac
if [[ -r "${SRC_DIR}/configure" ]] && \
[[ "${configure_with}" == 'undef' ]] || \
[[ "${configure_with}" == 'autotools' ]]; then
${SRC_DIR}/configure \
--prefix="${PREFIX}" \
"${CONFIGURE_ARGS[@]}" || \
std::die 3 \
"%s " "${module_name}/${module_version}:" \
"configure failed"
elif [[ -r "${SRC_DIR}/CMakeLists.txt" ]] && \
[[ "${configure_with}" == 'undef' ]] || \
[[ "${configure_with}" == "cmake" ]]; then
cmake \
-DCMAKE_INSTALL_PREFIX="${PREFIX}" \
"${CONFIGURE_ARGS[@]}" \
"${SRC_DIR}" || \
std::die 3 \
"%s " "${module_name}/${module_version}:" \
"cmake failed"
else
std::info \
"%s %s\n" \
"${module_name}/${module_version}:" \
"${FUNCNAME[0]}: skipping..."
fi
}
pbuild::post_configure() {
:
}
pbuild::pre_compile() {
:
}
pbuild::compile() {
make -j${JOBS}
}
pbuild::post_compile() {
:
}
pbuild::pre_install() {
:
}
pbuild::install() {
make install
}
pbuild::install_shared_libs() {
local -r binary="${PREFIX}/$1"
local -r pattern="${2//\//\\/}" # escape slash
local -r dstdir="${3:-${PREFIX}/lib}"
install_shared_libs_Linux() {
local libs=( $(ldd "${binary}" | \
awk "/ => \// && /${pattern}/ {print \$3}") )
cp -avL "${libs[@]}" "${dstdir}"
}
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}"))
cp -avL "${libs[@]}" "${dstdir}"
}
test -e "${binary}" || \
std::die 3 \
"%s " "${module_name}/${module_version}:" \
"${binary}: does not exist or is not executable!"
mkdir -p "${dstdir}"
case "${OS}" in
Linux )
install_shared_libs_Linux
;;
Darwin )
install_shared_libs_Darwin
;;
esac
}
pbuild::post_install() {
:
}
#
# The 'do it all' function.
#
pbuild::make_all() {
local -a runtime_dependencies=()
source "${BUILD_SCRIPT}"
set -e
local -r logfile="${BUILDBLOCK_DIR}/pbuild.log"
#
# To be able to set environment variables in one of the 'pbuild::TARGET'
# function we cannot use PIPE's like
# pbuild::configure | tee -a ...
#
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}."
}
#......................................................................
# non-redefinable post-install
post_install() {
install_doc() {
test -n "${MODULE_DOCFILES}" || return 0
local -r docdir="${PREFIX}/${_DOCDIR}/${module_name}"
std::info \
"%s %s\n" \
"${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
# Skip installation if modulefile does not exist.
install_pmodules_files() {
test -r "${BUILDBLOCK_DIR}/modulefile" || return 0
local -r target_dir="${PREFIX}/share/$GROUP/${module_name}"
install -m 0756 \
-d "${target_dir}/files"
install -m0444 \
"${BUILD_SCRIPT}" \
"${target_dir}"
install -m0444 \
"${BUILDBLOCK_DIR}/modulefile" \
"${target_dir}"
#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="${PREFIX}/.dependencies"
std::info \
"%s %s\n" \
"${module_name}/${module_version}:" \
"writing run-time dependencies to ${fname} ..."
local dep
echo -n "" > "${fname}"
for dep in "${runtime_dependencies[@]}"; 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
}
# sometimes we need an system depended post-install
post_install_linux() {
std::info \
"%s %s\n" \
"${module_name}/${module_version}:" \
"running post-installation for ${OS} ..."
cd "${PREFIX}"
# solve multilib problem with LIBRARY_PATH
# on 64bit Linux
[[ -d "lib" ]] && [[ ! -d "lib64" ]] && ln -s lib lib64
return 0
}
cd "${BUILD_DIR}"
[[ "${OS}" == "Linux" ]] && post_install_linux
install_doc
install_pmodules_files
write_runtime_dependencies
return 0
}
#......................................................................
# Install modulefile
install_modulefile() {
local -r src="${BUILDBLOCK_DIR}/modulefile"
if [[ ! -r "${src}" ]]; then
std::info \
"%s %s\n" \
"${module_name}/${module_version}:" \
"skipping modulefile installation ..."
return
fi
# assemble name of modulefile
local dst="${PMODULES_ROOT}/"
dst+="${GROUP}/"
dst+="${PMODULES_MODULEFILES_DIR}/"
dst+="${fully_qualified_module_name}"
# directory where to install modulefile
local -r dstdir=${dst%/*}
std::info \
"%s %s\n" \
"${module_name}/${module_version}:" \
"installing modulefile in '${dstdir}' ..."
mkdir -p "${dstdir}"
install -m 0444 "${src}" "${dst}"
}
install_release_file() {
local dst="${PMODULES_ROOT}/"
dst+="${GROUP}/"
dst+="${PMODULES_MODULEFILES_DIR}/"
dst+="${fully_qualified_module_name}"
# directory where to install release file
local -r dstdir=${dst%/*}
mkdir -p "${dstdir}"
local -r release_file="${dst%/*}/.release-${module_version}"
if [[ -r "${release_file}" ]]; then
local release
read release < "${release_file}"
if [[ "${release}" != "${module_release}" ]]; then
std::info \
"%s %s %s\n" \
"${module_name}/${module_version}:" \
"changing release from" \
"'${release}' to '${module_release}' ..."
echo "${module_release}" > "${release_file}"
fi
else
std::info \
"%s %s\n" \
"${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 %s\n" \
"${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 %s\n" \
"${module_name}/${module_version}:" \
"Cleaning up '${SRC_DIR}'..."
rm -rf "${SRC_DIR##*/}"
};
return 0
}
build_target() {
local dir="$1"
local target="$2"
if [[ -e "${BUILD_DIR}/.${target}" ]] && \
[[ ${force_rebuild} != 'yes' ]]; then
return 0
fi
local targets=()
targets+=( "pre_${target}_${system}" "pre_${target}_${OS}" "pre_${target}" )
targets+=( "${target}" )
targets+=( "post_${target}_${system}" "post_${target}_${OS}" "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}"
"pbuild::$t"
done
touch "${BUILD_DIR}/.${target}"
}
#......................................................................
# build module ${module_name}/${module_version}
build_module() {
echo C_INCLUDE_PATH=$C_INCLUDE_PATH
std::info \
"%s %s\n" \
"${module_name}/${module_version}:" \
"start building ..."
[[ ${dry_run} == yes ]] && std::die 0 ""
mkdir -p "${SRC_DIR}"
mkdir -p "${BUILD_DIR}"
std::info \
"%s %s\n" \
"${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 %s\n" \
"${module_name}/${module_version}:" \
"configuring ..."
build_target "${BUILD_DIR}" configure
[[ "${build_target}" == "configure" ]] && return 0
std::info \
"%s %s\n" \
"${module_name}/${module_version}:" \
"compiling ..."
build_target "${BUILD_DIR}" compile
[[ "${build_target}" == "compile" ]] && return 0
std::info \
"%s %s\n" \
"${module_name}/${module_version}:" \
"installing ..."
mkdir -p "${PREFIX}"
build_target "${BUILD_DIR}" install
post_install
[[ "${build_target}" == "install" ]] && return 0
install_modulefile
install_release_file
cleanup_build
cleanup_src
std::info \
"%s %s\n" \
"${module_name}/${module_version}:" \
"Done ..."
return 0
}
remove_module() {
if [[ -d "${PREFIX}" ]]; then
std::info \
"%s %s\n" \
"${module_name}/${module_version}:" \
"removing all files in '${PREFIX}' ..."
[[ "${dry_run}" == 'no' ]] && rm -rf ${PREFIX}
fi
# assemble name of modulefile
local dst="${PMODULES_ROOT}/"
dst+="${GROUP}/"
dst+="${PMODULES_MODULEFILES_DIR}/"
dst+="${fully_qualified_module_name}"
# directory where to install modulefile
local -r dstdir=${dst%/*}
if [[ -e "${dst}" ]]; then
std::info \
"%s %s\n" \
"${module_name}/${module_version}:" \
"removing modulefile '${dst}' ..."
[[ "${dry_run}" == 'no' ]] && rm -v "${dst}"
fi
local release_file="${dstdir}/.release-${module_version}"
if [[ -e "${release_file}" ]]; then
std::info \
"%s %s\n" \
"${module_name}/${module_version}:" \
"removing release file '${release_file}' ..."
[[ "${dry_run}" == 'no' ]] && rm -v "${release_file}"
fi
rmdir -p "${dstdir}" 2>/dev/null || :
}
########################################################################
#
# here we really start with make_all()
#
# setup module specific environment
if [[ "${bootstrap}" == 'no' ]]; then
check_supported_systems
set_full_module_name_and_prefix
if [[ "${module_release}" == 'removed' ]]; then
remove_module
elif [[ ! -d "${PREFIX}" ]] || \
[[ "${force_rebuild}" == 'yes' ]]; then
build_module
else
std::info \
"%s %s\n" \
"${module_name}/${module_version}:" \
"already exists, not rebuilding ..."
if [[ "${opt_update_modulefiles}" == "yes" ]]; then
install_modulefile
fi
install_release_file
fi
else
build_module
fi
return 0
}
pbuild.init_env() {
#......................................................................
#
# parse the passed version string
#
# the following global variables will be set in this function:
# V_MAJOR
# V_MINOR
# V_PATCHLVL
# V_RELEASE
# USE_FLAGS
#
parse_version() {
local v="$1"
V_MAJOR='' # first number in version string
V_MINOR='' # second number in version string (or empty)
V_PATCHLVL='' # third number in version string (or empty)
V_RELEASE='' # module release (or empty)
USE_FLAGS='' # architectures (or empty)
local tmp=''
if [[ "$v" =~ "_" ]]; then
tmp="${v#*_}"
USE_FLAGS=":${tmp//_/:}:"
v="${v%%_*}"
fi
V_PKG="${v%%-*}" # version without the release number
V_RELEASE="${v#*-}" # release number
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
}
local -r module_name="$1"
local -r module_version="$2"
SRC_DIR="${TEMP_DIR}/${module_name}-${module_version}/src"
BUILD_DIR="${TEMP_DIR}/${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=()
PATCH_FILES=()
PATCH_STRIPS=()
PATCH_STRIP_DEFAULT='1'
MODULE_DOCFILES=()
configure_with='undef'
}
pbuild.build_module() {
module_name="$1"
module_version="$2"
module_release="$3"
shift 3
with_modules=( "$@" )
# used in pbuild::make_all
declare bootstrap='no'
#......................................................................
#
# test whether a module is loaded or not
#
# $1: module name
#
is_loaded() {
[[ :${LOADEDMODULES}: =~ :$1: ]]
}
#......................................................................
#
# build a dependency
#
# $1: name of module to build
#
# :FIXME: needs testing
#
build_dependency() {
#..............................................................
#
# Test whether a module with the given name already exists.
#
# Arguments:
# $1: module name
#
# Notes:
# The passed module name should be NAME/VERSION
# :FIXME: this does not really work in a hierarchical group
# without adding the dependencies...
#
module_exists() {
[[ -n $("${MODULECMD}" bash search -a --no-header "$1" \
2>&1 1>/dev/null) ]]
}
local -r m=$1
std::debug "${m}: module not available"
local rels=( ${PMODULES_DEFINED_RELEASES//:/ } )
[[ ${dry_run} == yes ]] && \
std::die 1 \
"%s " \
"${m}: module does not exist," \
"cannot continue with dry run..."
std::info "%s\n" \
"$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!"
"${buildscript}" "${m#*/}" ${args[@]}
module_exists "$m" || \
std::die 1 \
"$m: oops: build failed..."
}
#......................................................................
#
# Load build- and run-time dependencies.
#
# Arguments:
# none
#
# Variables
# [r] module_release set if defined in a variants file
# runtime_dependencies runtime dependencies from variants added
#
load_build_dependencies() {
local m=''
for m in "${with_modules[@]}"; do
# module name prefixes in dependency declarations:
# 'b:' this is a build dependency
# 'r:' this a run-time dependency, *not* required for
# building
# without prefix: this is a build and
# run-time dependency
if [[ "${m:0:2}" == "b:" ]]; then
m=${m#*:} # remove 'b:'
elif [[ "${m:0:2}" == "r:" ]]; then
m=${m#*:} # remove 'r:'
runtime_dependencies+=( "$m" )
else
runtime_dependencies+=( "$m" )
fi
is_loaded "$m" && continue
# 'module avail' might output multiple matches if module
# name and version are not fully specified or in case
# modules with and without a release number exist.
# Example:
# mpc/1.1.0 and mpc/1.1.0-1. Since we get a sorted list
# from 'module avail' and the full version should be set
# in the variants file, we look for the first exact
# match.
local release_of_dependency=''
if ! pbuild::module_is_avail "$m" release_of_dependency; then
build_dependency "$m"
pbuild::module_is_avail "$m" release_of_dependency || \
std::die 6 "Oops"
fi
# should be set, just in case it is not...
: ${release_of_dependency:='unstable'}
# for a stable module all dependencies must be stable
if [[ "${module_release}" == 'stable' ]] \
&& [[ "${release_of_dependency}" != 'stable' ]]; then
std::die 5 \
"%s " "${module_name}/${module_version}:" \
"release cannot be set to '${module_release}'" \
"since the dependency '$m' is ${release_of_dependency}"
# for a unstable module no dependency must be deprecated
elif [[ "${module_release}" == 'unstable' ]] \
&& [[ "${release_of_dependency}" == 'deprecated' ]]; then
std::die 5 \
"%s " "${module_name}/${module_version}:" \
"release cannot be set to '${module_release}'" \
"since the dependency '$m' is ${release_of_dependency}"
fi
echo "Loading module: ${m}"
module load "${m}"
done
}
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
load_build_dependencies
pbuild.init_env "${module_name}" "${module_version}"
pbuild::make_all
}
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
}
# Local Variables:
# mode: sh
# sh-basic-offset: 8
# tab-width: 8
# End: