Files
MX_Pmodule/lib/libem.bash

725 lines
17 KiB
Bash

#!/bin/bash
PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/opt/X11/bin
shopt -s expand_aliases
declare -r BUILDSCRIPT=$( cd $(dirname "$0") && pwd )/$(basename "$0")
declare -rx ARGS="$@"
declare -rx SHLIBDIR=$( cd $(dirname "$BASH_SOURCE") && pwd )
declare -r OS=$(uname -s)
# number of parallel make jobs
declare -i JOBS=3
source "${SHLIBDIR}/lib.bash"
declare -rx BUILD_BASEDIR=$(abspath $SHLIBDIR/..)
source "$(readlink ${BUILD_BASEDIR}/config/environment.bash)"
declare -xr BUILD_CONFIGDIR="${BUILD_BASEDIR}/config"
declare -xr BUILD_SCRIPTSDIR="${BUILD_BASEDIR}/scripts"
declare -xr BUILD_TMPDIR="${BUILD_BASEDIR}/tmp"
declare -xr BUILD_DOWNLOADSDIR="${BUILD_BASEDIR}/Downloads"
declare -xr BUILD_VERSIONSFILE="${BUILD_CONFIGDIR}/versions.conf"
declare -xr PSI_TEMPLATES_DIR='templates'
if [[ -z "${BUILD_CONFIGDIR}/families.d/"*.conf ]]; then
die 1 "Default family configuration not set in ${BUILD_CONFIGDIR}/families.d"
fi
for f in "${BUILD_CONFIGDIR}/families.d/"*.conf; do
source "${f}"
done
declare -x PREFIX=''
declare -x DOCDIR=''
declare -x MODULE_FAMILY=''
declare -x MODULE_RELEASE=''
declare cur_module_release=''
declare DEPEND_RELEASE=''
declare -x MODULE_NAME=''
# these directories are module dependend
declare -x MODULE_SRCDIR=''
declare -x MODULE_BUILDDIR=''
declare -x MODULE_BUILD_DEPENDENCIES
declare -x MODULE_DEPENDENCIES
declare -x C_INCLUDE_PATH
declare -x CPLUS_INCLUDE_PATH
declare -x CPP_INCLUDE_PATH
declare -x LIBRARY_PATH
declare -x LD_LIBRARY_PATH
declare -x DYLD_LIBRARY_PATH
function usage() {
error "
Usage: $0 [OPTIONS..] [VERSION] [ENV=VALUE...]
VERSION
Version of module to compile.
ENV=VALUE
Set environment variable ENV to VALUE. This can be used to
overwrite default versions.
-? | -h | --help
Print usage
-v | --verbose )
Verbose output
-j N | --jobs=N
Run N parallel make jobs
-f | --force-rebuild
Force rebuild of module.
-b | --bootstrap
Bootstrap Pmodules
--with=P/V
Preload module P with version V. To preload multiple modules,
use this option per module. Nete that order may matter.
--release=stable|unstable|deprecated
"
exit 1
}
debug_on='no'
force_rebuild='no'
ENVIRONMENT_ARGS=''
dry_run='no'
bootstrap='no'
# array collecting all modules specified on the command line via '--with=module'
with_modules=()
while (( $# > 0 )); do
case $1 in
-j )
JOBS=$2
shift
;;
--jobs=[0-9]* )
JOBS=${1/--jobs=}
;;
-v | --verbose)
debug_on='yes'
;;
-f | --force-rebuild )
force_rebuild='yes'
;;
-b | --bootstrap )
bootstrap='yes'
force_rebuild='yes'
;;
-? | -h | --help )
usage
;;
--dry-run )
dry_run='yes'
;;
--release=* )
MODULE_RELEASE=${1/--release=}
;;
--with=*/* )
with_modules+=( ${1/--with=} )
;;
*=* )
eval $1
ENVIRONMENT_ARGS="${ENVIRONMENT_ARGS} $1"
;;
* )
V=$1
;;
esac
shift
done
if [[ ${debug_on} == yes ]]; then
trap 'echo "$BASH_COMMAND"' DEBUG
fi
# while bootstraping the module command is not yet available
if [[ ${bootstrap} == no ]]; then
source "${PSI_PREFIX}/${PSI_CONFIG_DIR}/profile.bash"
MODULECMD="${PMODULES_HOME}/bin/modulecmd"
[[ -x ${MODULECMD} ]] || die 1 "${MODULECMD}: no such executable"
module use unstable
module purge
fi
P=$(basename $(dirname "${BUILDSCRIPT}"))
_P=$(echo $P | tr [:lower:] [:upper:])
_P=${_P//-/_}
_V=${_P}_VERSION
eval "${ENVIRONMENT_ARGS}"
if [[ -n ${PSI_RELEASES} ]]; then
declare -r releases="${PSI_RELEASES}"
else
# set defaults, if file doesn't exist or isn't readable
declare -r releases=":unstable:stable:deprecated:"
fi
is_release () {
[[ ${releases} =~ :$1: ]]
}
function em.set_release() {
is_release "$1" || die 1 "$P: unknown release type: $1"
[[ "$1" == "deprecated" ]] && die 0 "$P: is deprecated, we don't rebuild it."
MODULE_RELEASE="$1"
}
function em.supported_os() {
for os in "$@"; do
[[ ${os} == ${OS} ]] && return 0
done
die 0 "${P}: Not available for ${OS}."
}
function em.add_to_family() {
if [[ -z ${1} ]]; then
die 42 "${FUNCNAME}: Missing family argument."
fi
if [[ ! -d ${PSI_PREFIX}/${PSI_MODULES_ROOT}/${1} ]]; then
die 43 "${1}: family does not exist."
fi
MODULE_FAMILY=$1
}
function em.set_build_dependencies() {
MODULE_BUILD_DEPENDENCIES=("$@")
}
function em.set_runtime_dependencies() {
MODULE_DEPENDENCIES=("$@")
}
function em.set_supported_compilers() {
MODULE_SUPPORTED_COMPILERS=("$@")
}
function em.set_docfiles() {
MODULE_DOCFILES=("$@")
}
function module_is_available() {
[[ -n $("${MODULECMD}" bash avail "$1" 2>&1 1>/dev/null) ]]
}
function _load_build_dependencies() {
# :FIXME: merge this two loops, load only modules which are required
# this merge is not as easy as it looks like at a first glance!
for m in "${with_modules[@]}"; do
if module_is_available "$m"; then
module load "${m}"
else
die 44 "$m: module not available!"
fi
done
for m in "${MODULE_BUILD_DEPENDENCIES[@]}"; do
[[ -z $m ]] && continue
if [[ ! $m =~ "*/*" ]]; then
local _V=$(echo -n $m | tr [:lower:] [:upper:] )_VERSION
if [[ -n ${!_V} ]]; then
m=$m/${!_V}
else
echo "$m: warning: No version set, loading default ..."
fi
fi
if ! module_is_available "$m"; then
debug "${m}: module not available"
local rels=( ${releases//: } )
for rel in "${rels[@]}"; do
debug "${m}: check release \"${rel}\""
eval $("${MODULECMD}" bash use ${rel})
if module_is_available "${m}"; then
die 1 "${m}: module available with release \"${rel}\", add this release with \"module use ${rel}\" and re-run build script."
fi
done
[[ ${dry_run} == yes ]] && {
die 1 "${m}: module does not exist, cannot continue with dry run..."
}
echo "$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 )
;;
--release=* )
args+=( $1 )
;;
--with=*/* )
args+=( $1 )
;;
*=* )
args+=( $1 )
;;
esac
shift
done
"${BUILD_SCRIPTSDIR}"/*/"${m/\/*}/build" ${args[@]}
if [[ -z $(module avail "$m" 2>&1) ]]; then
die 1 "$m: oops: build failed..."
fi
fi
local modulepath_root="${PSI_PREFIX}/${PSI_MODULES_ROOT}"
local tmp=$( module display "${m}" 2>&1 | grep -m1 -- "${modulepath_root}" )
tmp=${tmp/${modulepath_root}\/}
tmp=${tmp%%/*}
local _family=( ${tmp//./ } )
if [[ ${_family[1]} == deprecated ]]; then
# set module release to 'deprecated' if a build dependency
# is deprecated
DEPEND_RELEASE='deprecated'
elif [[ ${_family[1]} == unstable ]] && [[ -z ${DEPEND_RELEASE} ]]; then
# set module release to 'unstable' if a build dependency is
# unstable and release not yet set
DEPEND_RELEASE='unstable'
fi
echo "Loading module: ${m}"
module load "${m}"
done
}
if [[ ${bootstrap} == yes ]]; then
function _load_build_dependencies() {
:
}
fi
function _write_runtime_dependencies() {
local -r fname="${PREFIX}/.dependencies"
info "Writing run-time dependencies to ${fname}"
local dep
echo -n "" > "${fname}"
for dep in "${MODULE_DEPENDENCIES[@]}"; do
[[ -z $dep ]] && continue
if [[ ! $dep =~ .*/.* ]]; then
local _V=$(echo -n $dep | tr [:lower:] [:upper:] )_VERSION
dep=$dep/${!_V}
fi
echo "${dep}" >> "${fname}"
done
}
function _write_build_dependencies() {
local -r fname="${PREFIX}/.build_dependencies"
info "Writing build dependencies to ${fname}"
local dep
echo -n "" > "${fname}"
for dep in "${MODULE_BUILD_DEPENDENCIES[@]}"; do
[[ -z $dep ]] && continue
if [[ ! $dep =~ "*/*" ]]; then
local _V=$(echo -n $dep | tr [:lower:] [:upper:] )_VERSION
dep=$dep/${!_V}
fi
echo "${dep}" >> "${fname}"
done
}
# setup general environment
function _setup_env1() {
C_INCLUDE_PATH=''
CPLUS_INCLUDE_PATH=''
CPP_INCLUDE_PATH=''
LIBRARY_PATH=''
LD_LIBRARY_PATH=''
DYLD_LIBRARY_PATH=''
CFLAGS=''
CPPFLAGS=''
CXXFLAGS=''
LIBS=''
LDFLAGS=''
while read _name _version; do
[[ -z ${_name} ]] && continue
[[ -z ${_version} ]] && continue
[[ "${_name:0:1}" == '#' ]] && continue
_NAME=$(echo ${_name} | tr [:lower:] [:upper:])
eval ${_NAME}_VERSION=$_version
done < "${BUILD_VERSIONSFILE}"
}
#setup module specific environment
function _setup_env2() {
if [[ -z ${MODULE_FAMILY} ]]; then
die 1 "$P: family not set."
fi
# overwrite environment variables with values we got on the cmd line
eval "${ENVIRONMENT_ARGS}"
# this allows us to specify the version as PKGNAME_VERSION=1.2.3 on
# the cmd-line
if [[ -z $V ]]; then
V=$(eval echo \$${_P}_VERSION)
fi
# oops, we need a version
if [[ -z $V ]]; then
die 1 "$P: Missing version."
fi
MODULE_SRCDIR="${BUILD_TMPDIR}/src/${P/_serial}-$V"
MODULE_BUILDDIR="${BUILD_TMPDIR}/build/$P-$V"
# build module name
# :FIXME: the MODULE_PREFIX should be derived from MODULE_NAME
# :FIXME: this should be read from a configuration file
case ${MODULE_FAMILY} in
Tools )
MODULE_RPREFIX="${P}/${V}"
MODULE_NAME="${P}/${V}"
;;
Programming )
MODULE_RPREFIX="${P}/${V}"
MODULE_NAME="${P}/${V}"
;;
Libraries )
MODULE_RPREFIX="${P}/${V}"
MODULE_NAME="${P}/${V}"
;;
System )
MODULE_RPREFIX="${P}/${V}"
MODULE_NAME="${P}/${V}"
;;
Compiler )
MODULE_RPREFIX="${P}/${V}"
MODULE_RPREFIX+="/${COMPILER}/${COMPILER_VERSION}"
MODULE_NAME="${COMPILER}/${COMPILER_VERSION}/"
MODULE_NAME+="${P}/${V}"
;;
MPI )
MODULE_RPREFIX="${P}/${V}/"
MODULE_RPREFIX+="${MPI}/${MPI_VERSION}/"
MODULE_RPREFIX+="${COMPILER}/${COMPILER_VERSION}"
MODULE_NAME="${COMPILER}/${COMPILER_VERSION}/"
MODULE_NAME+="${MPI}/${MPI_VERSION}/"
MODULE_NAME+="${P}/${V}"
;;
HDF5 )
MODULE_RPREFIX="${P}/${V}"
MODULE_RPREFIX+="/${HDF5}/${HDF5_VERSION}"
MODULE_RPREFIX+="/${MPI}/${MPI_VERSION}"
MODULE_RPREFIX+="/${COMPILER}/${COMPILER_VERSION}"
MODULE_NAME="${COMPILER}/${COMPILER_VERSION}/"
MODULE_NAME+="${MPI}/${MPI_VERSION}/"
MODULE_NAME+="${HDF5}/${HDF5_VERSION}/"
MODULE_NAME+="${P}/${V}"
;;
HDF5_serial )
MODULE_RPREFIX="${P}/${V}"
MODULE_RPREFIX+="/hdf5_serial/${HDF5_SERIAL_VERSION}"
MODULE_RPREFIX+="/${COMPILER}/${COMPILER_VERSION}"
MODULE_NAME="${COMPILER}/${COMPILER_VERSION}/"
MODULE_NAME+="hdf5_serial/${HDF5_VERSION}/"
MODULE_NAME+="${P}/${V}"
;;
* )
die 1 "$P: oops: unknown family: ${MODULE_FAMILY}"
;;
esac
# set PREFIX of module
PREFIX="${PSI_PREFIX}/${MODULE_FAMILY}/${MODULE_RPREFIX}"
# get module release if already installed
local saved_modulepath=${MODULEPATH}
rels=( ${releases//:/ } )
for rel in "${rels[@]}"; do
eval $("${MODULECMD}" bash unuse ${rel})
done
for rel in "${rels[@]}"; do
eval $("${MODULECMD}" bash use ${rel})
if module_is_available "${P}/${V}"; then
cur_module_release=${rel}
info "${P}/${V}: already available and released as \"${rel}\""
break
fi
done
MODULEPATH=${saved_modulepath}
# set release of module
# release is deprecated
# - if a build-dependency is deprecated or
# - the module already exists and is deprecated or
# - is forced to be deprecated by setting this on the command line
if [[ "${depend_release}" == 'deprecated' ]] || \
[[ "${cur_module_release}" == 'deprecated' ]] \
|| [[ "${MODULE_RELEASE}" == 'deprecated' ]]; then
MODULE_RELEASE='deprecated'
info "${P}/${V}: will be released as \"deprecated\""
#
# release is stable
# - if all build-dependency are stable or
# - the module already exists and is stable
# - an unstable release of the module exists and the release is
# changed to stable on the command line
elif [[ "${depend_release}" == 'stable' ]] \
|| [[ "${cur_module_release}" == 'stable' ]] \
|| [[ "${MODULE_RELEASE}" == 'stable' ]]; then
MODULE_RELEASE='stable'
info "${P}/${V}: will be released as \"stable\""
#
# release is unstable
# - if a build-dependency is unstable or
# - if the module does not exists and no other release-type is
# given on the command line
# - and all the cases I didn't think of
else
MODULE_RELEASE='unstable'
info "${P}/${V}: will be released as \"unstable\""
fi
# directory for README's, license files etc
DOCDIR="${PREFIX}/share/doc/$P"
# set tar-ball and flags for tar
TARBALL="${BUILD_DOWNLOADSDIR}/${P/_serial}"
if [[ -r "${TARBALL}-${V}.tar.gz" ]]; then
TARBALL+="-${V}.tar.gz"
elif [[ -r "${TARBALL}-${OS}-${V}.tar.gz" ]]; then
TARBALL+="-${OS}-${V}.tar.gz"
elif [[ -r "${TARBALL}-${V}.tar.bz2" ]]; then
TARBALL+="-${V}.tar.bz2"
elif [[ -r "${TARBALL}-${OS}-${V}.tar.bz2" ]]; then
TARBALL+="-${OS}-${V}.tar.bz2"
else
error "tar-ball for $P/$V not found."
exit 43
fi
}
if [[ ${bootstrap} == yes ]]; then
# redefine function for bootstrapping
function _setup_env2() {
if [[ -z ${MODULE_FAMILY} ]]; then
die 1 "$P: family not set."
fi
if [[ -z $V ]]; then
V=$(eval echo \$${_P}_VERSION)
fi
# oops, we need a version
if [[ -z $V ]]; then
die 1 "$P: Missing version."
fi
MODULE_SRCDIR="${BUILD_TMPDIR}/src/${P/_serial}-$V"
MODULE_BUILDDIR="${BUILD_TMPDIR}/build/$P-$V"
MODULE_FAMILY='Tools'
MODULE_NAME="Pmodules/${PMODULES_VERSION}"
# set PREFIX of module
PREFIX="${PSI_PREFIX}/${MODULE_FAMILY}/${MODULE_NAME}"
MODULE_RELEASE='unstable'
info "${MODULE_NAME}: will be released as \"${MODULE_RELEASE}\""
# directory for README's, license files etc
DOCDIR="${PREFIX}/share/doc/$P"
# set tar-ball and flags for tar
TARBALL="${BUILD_DOWNLOADSDIR}/${P/_serial}"
if [[ -r "${TARBALL}-${V}.tar.gz" ]]; then
TARBALL+="-${V}.tar.gz"
elif [[ -r "${TARBALL}-${OS}-${V}.tar.gz" ]]; then
TARBALL+="-${OS}-${V}.tar.gz"
elif [[ -r "${TARBALL}-${V}.tar.bz2" ]]; then
TARBALL+="-${V}.tar.bz2"
elif [[ -r "${TARBALL}-${OS}-${V}.tar.bz2" ]]; then
TARBALL+="-${OS}-${V}.tar.bz2"
else
error "tar-ball for $P/$V not found."
exit 43
fi
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"
}
fi
function _prep() {
# untar sources
if [[ ! -d ${MODULE_SRCDIR} ]]; then
mkdir -p "${BUILD_TMPDIR}/src"
(cd "${BUILD_TMPDIR}/src" && tar xvf "${TARBALL}")
fi
# create build directory
mkdir -p "${MODULE_BUILDDIR}"
}
function em.pre_configure() {
:
}
function em.configure() {
${MODULE_SRCDIR}/configure \
--prefix="${PREFIX}"
}
function em.build() {
make -j${JOBS}
}
function em.install() {
make install
}
function em.post_install() {
:
}
function em.install_doc() {
info "Installing documentation to ${DOCDIR}"
install -m 0755 -d "${DOCDIR}"
install -m0444 "${MODULE_DOCFILES[@]/#/${MODULE_SRCDIR}/}" "${BUILDSCRIPT}" "${DOCDIR}"
}
function _set_link() {
local -r link_name="${PSI_PREFIX}/${PSI_MODULES_ROOT}/${MODULE_FAMILY}/${MODULE_NAME}"
local -r dir_name=${link_name%/*}
local -r release_file="${dir_name}/.release-${MODULE_NAME##*/}"
if [[ ! -e "${_path}" ]]; then
(
info "Setting new sym-link \"${link_name}\" ..."
mkdir -p "${dir_name}"
cd "${dir_name}"
local x
IFS='/' x=( ${dir_name/${PSI_PREFIX}\/${PSI_MODULES_ROOT}\/} )
local n=${#x[@]}
local -r _target="../"$(eval printf "../%.s" {1..${n}})${PSI_TEMPLATES_DIR##*/}/"${MODULE_FAMILY}/${P}/modulefile"
ln -fs "${_target}" "${MODULE_NAME##*/}"
)
fi
info "${MODULE_NAME}: set release to '${MODULE_RELEASE}'"
echo "${MODULE_RELEASE}" > "${release_file}"
}
function em.cleanup_build() {
(
[[ -d /${MODULE_BUILDDIR} ]] || return 0
cd "/${MODULE_BUILDDIR}/..";
if [[ $(pwd) != / ]]; then
echo "Cleaning up $(pwd)/${COMPILER_VERSION}"
rm -rf *
fi
);
}
function em.cleanup_src() {
(
[[ -d /${MODULE_SRCDIR} ]] || return 0
cd "/${MODULE_SRCDIR}/..";
if [[ $(pwd) != / ]]; then
echo "Cleaning up $(pwd)"
rm -rf ${MODULE_SRCDIR##*/}
fi
);
return 0
}
function _check_compiler() {
test -z ${MODULE_SUPPORTED_COMPILERS} && return 0
for cc in ${MODULE_SUPPORTED_COMPILERS[@]}; do
if [[ ${COMPILER}/${COMPILER_VERSION} =~ ${cc} ]]; then
return 0
fi
done
die 0 "Package cannot be build with ${COMPILER}/${COMPILER_VERSION}."
}
# unfortunatelly we need sometime an OS depended post-install
function _post_install_linux() {
cd "${PREFIX}"
# solve multilib problem with LIBRARY_PATH on 64bit Linux
[[ -d "lib" ]] && [[ ! -d "lib64" ]] && ln -s lib lib64
return 0
}
function _post_install() {
info "Run post-installation for ${OS} ..."
[[ "${OS}" == "Linux" ]] && _post_install_linux
info "Post-installation done ..."
return 0
}
function em.make_all() {
echo "${P}:"
_setup_env1
_load_build_dependencies
# setup module specific environment
_setup_env2
if [[ ! -d "${PREFIX}" ]] || [[ ${force_rebuild} == 'yes' ]]; then
echo "Building $P/$V ..."
[[ ${dry_run} == yes ]] && die 0 ""
_check_compiler
_prep
cd "${MODULE_SRCDIR}"
em.pre_configure
cd "${MODULE_BUILDDIR}"
em.configure
em.build
em.install
em.post_install
em.install_doc
_post_install
if [[ ${bootstrap} == 'no' ]]; then
_write_runtime_dependencies
_write_build_dependencies
fi
else
echo "Not rebuilding $P/$V ..."
fi
if [[ ${bootstrap} == 'no' ]]; then
_set_link
fi
em.cleanup_build
}
# Local Variables:
# mode: sh
# sh-basic-offset: 8
# tab-width: 8
# End: