Files
MX_Pmodule/lib/libem.bash

608 lines
15 KiB
Bash

#!/bin/bash
PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/opt/X11/bin
declare -r MODULECMD="${MODULESHOME}/bin/modulecmd"
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"
# while bootstraping the module command is not yet available
if typeset -f module > /dev/null 2>&1 ; then
module purge
fi
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"
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
if [[ $DEBUG_ON ]]; then
trap 'echo "$BASH_COMMAND"' DEBUG
fi
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.
--with-compiler=P/V
Use compiler P with version V
--with-mpi=P/V
Use MPI implementation P with version V
--with-hdf5=V
Use parallel HDF5 version V
--with-hdf5_serial=V
Use serial HDF5 version V
"
exit 1
}
P=$(basename $0)
P=${P%.*}
_P=$(echo $P | tr [:lower:] [:upper:])
_V=${_P}_VERSION
DEBUG_ON=''
FORCE_REBUILD=''
ENVIRONMENT_ARGS=''
WITH_ARGS=''
DRY_RUN=''
while (( $# > 0 )); do
case $1 in
-j )
JOBS=$2
shift
;;
--jobs=[0-9]* )
JOBS=${1/--jobs=}
;;
-v | --verbose)
DEBUG_ON=':'
;;
-f | --force-rebuild )
FORCE_REBUILD=':'
;;
-? | -h | --help )
usage
;;
--dry-run )
DRY_RUN='dry-run'
;;
--release=* )
MODULE_RELEASE=${1/--release=}
if [[ -n ${MODULE_RELEASE} ]] && [[ ${MODULE_RELASE:0:1} != . ]]; then
MODULE_RELEASE=".${MODULE_RELEASE}"
fi
;;
--with-hdf5=*)
v=${1/--with-hdf5=}
ENVIRONMENT_ARGS="${ENVIRONMENT_ARGS} HDF5=hdf5 HDF5_VERSION=$v"
;;
--with-hdf5_serial=*)
v=${1/--with-hdf5_serial=}
ENVIRONMENT_ARGS="${ENVIRONMENT_ARGS} HDF5_SERIAL=hdf5_serial HDF5_SERIAL_VERSION=$v"
;;
--with-*=* )
# --with-mpi=openmpi/1.6.5 ->
# MPI=openmpi MPI_VERSION=1.6.5 OPENMPI_VERSION=1.6.5
arg=${1/--with-}
f=$(echo ${arg/=*} | tr [:lower:] [:upper:])
m=${arg/*=}
if [[ -z $m ]]; then
error "$1: module missing."
die 1
fi
p=${m/\/*}
_p=$(echo ${p} | tr [:lower:] [:upper:])
v=${m/*\/}
if [[ -z $v ]] || [[ $p == $v ]]; then
error "$1: version missing in module specification."
die 1
fi
ENVIRONMENT_ARGS="${ENVIRONMENT_ARGS} ${f}=${p} ${f}_VERSION=$v ${_p}_VERSION=${v}"
;;
* )
if [[ $1 =~ = ]]; then
eval $1
ENVIRONMENT_ARGS="${ENVIRONMENT_ARGS} $1"
else
V=$1
fi
;;
esac
shift
done
eval "${ENVIRONMENT_ARGS}"
function is_release () {
local -a releases=( '' )
releases+=( $(< "${PSI_PREFIX}/${PSI_RELEASES_CONF}") )
local rel
for rel in "${releases[@]}"; do
[[ "${rel}" == "$1" ]] && return 0
done
return 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_CONFIG_DIR}/${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 is_module_available() {
[[ -n $("${MODULECMD}" bash avail "$1" 2>&1 1>/dev/null) ]]
}
function _load_build_dependencies() {
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 [[ -z $("${MODULECMD}" bash avail "$m" 2>&1 1>/dev/null) ]]; then
debug "${m}: module not available"
for rel in $(< "${PSI_PREFIX}/${PSI_RELEASES_CONF}"); do
debug "${m}: check release \"${rel/.}\""
eval $("${PSI_PREFIX}/${PSI_CONFIG_DIR}/init/extensions/use.bash" ${rel/.})
if is_module_available "${m}"; then
die 1 "${m}: module available in release \"${rel/.}\", add this release with \"module use ${rel/.}\" and re-run build script."
fi
done
echo "$m: info: module does not exist, trying to build it..."
"${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//./ } )
# set module release to 'deprecated' if a build dependency
# is deprecated
if [[ ${_family[1]} == deprecated ]]; then
DEPEND_RELEASE='.deprecated'
# set module release to 'unstable' if a build dependency is
# unstable and release not yet set
elif [[ ${_family[1]} == unstable ]] && [[ -z ${DEPEND_RELEASE} ]]; then
DEPEND_RELEASE='.unstable'
fi
echo "Loading module: ${m}"
module load "${m}"
done
}
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=''
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/$COMPILER/$COMPILER_VERSION"
# 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}/${COMPILER}/${COMPILER_VERSION}"
MODULE_NAME="${COMPILER}/${COMPILER_VERSION}/${P}/${V}"
;;
MPI )
MODULE_RPREFIX="${P}/${V}/${MPI}/${MPI_VERSION}/${COMPILER}/${COMPILER_VERSION}"
MODULE_NAME="${COMPILER}/${COMPILER_VERSION}/${MPI}/${MPI_VERSION}/${P}/${V}"
;;
HDF5 )
MODULE_RPREFIX="${P}/${V}/${HDF5}/${HDF5_VERSION}/${MPI}/${MPI_VERSION}/${COMPILER}/${COMPILER_VERSION}/"
MODULE_NAME="${COMPILER}/${COMPILER_VERSION}/${MPI}/${MPI_VERSION}/${HDF5}/${HDF5_VERSION}/${P}/${V}"
;;
HDF5_serial )
MODULE_RPREFIX="${P}/${V}/hdf5_serial/${HDF5_SERIAL_VERSION}/${COMPILER}/${COMPILER_VERSION}"
MODULE_NAME="${COMPILER}/${COMPILER_VERSION}/hdf5_serial/${HDF5_VERSION}/${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}
for rel in $(< "${PSI_PREFIX}/${PSI_RELEASES_CONF}"); do
eval $("${PSI_PREFIX}/${PSI_CONFIG_DIR}/init/extensions/unuse.bash" ${rel/.})
done
for rel in '.stable' $(< "${PSI_PREFIX}/${PSI_RELEASES_CONF}"); do
if [[ "${rel}" != '.stable' ]]; then
eval $("${PSI_PREFIX}/${PSI_CONFIG_DIR}/init/extensions/use.bash" ${rel/.})
fi
if is_module_available "${P}/${V}"; then
cur_module_release=${rel}
info "${P}/${V}: already available and is in release \"${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}-$V.tar"
if [[ -r $TARBALL.gz ]]; then
TARBALL=${TARBALL}.gz
_UNTAR_FLAGS='xvzf'
elif [[ -r ${TARBALL}.bz2 ]]; then
TARBALL=${TARBALL}.bz2
_UNTAR_FLAGS='xvjf'
else
error "tar-ball for $P/$V not found."
exit 43
fi
}
function _prep() {
# untar sources
if [[ ! -d ${MODULE_SRCDIR} ]]; then
mkdir -p "${BUILD_TMPDIR}/src"
(cd "${BUILD_TMPDIR}/src" && tar ${_UNTAR_FLAGS} "${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() {
[[ ${cur_module_release} == ${MODULE_RELEASE} ]] && return 0
local _path
if [[ "${cur_module_release}" != '' ]]; then
_path="${PSI_PREFIX}/${PSI_MODULES_ROOT}/${MODULE_FAMILY}${cur_module_release/.stable}/${MODULE_NAME}"
info "Removing old sym-link \"${_path}\" ..."
rm "${_path}"
fi
(
_path="${PSI_PREFIX}/${PSI_MODULES_ROOT}/${MODULE_FAMILY}${MODULE_RELEASE/.stable}/${MODULE_NAME%/*}"
info "Setting new sym-link \"${_path}/${V}\" ..."
mkdir -p "${_path}"
cd "${_path}"
local x
IFS='/' x=( ${_path/${PSI_PREFIX}\/${PSI_MODULES_ROOT}\/} )
local n=${#x[@]}
local -r _target="../"$(eval printf "../%.s" {1..${n}})${PSI_CONFIG_DIR##*/}/"${MODULE_FAMILY}/${P}/modulefile"
ln -fs "${_target}" "${MODULE_NAME##*/}"
)
}
function _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
);
}
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
}
function _post_install() {
info "Run post-installation for ${OS}"
[[ "${OS}" == "Linux" ]] && _post_install_linux
return 0
}
function em.make_all() {
echo "${P}:"
_setup_env1
_load_build_dependencies
# setup module specific environment
_setup_env2
if [[ ! -d "${PREFIX}" ]] || [[ ${FORCE_REBUILD} ]]; then
echo "Building $P/$V ..."
[[ "${DRY_RUN}" ]] && 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
_write_runtime_dependencies
_write_build_dependencies
else
echo "Not rebuilding $P/$V ..."
fi
_set_link
_cleanup_build
}
# Local Variables:
# mode: sh
# sh-basic-offset: 8
# tab-width: 8
# End: