From 0457ac985c95bbef2845f020e3ee4dffee01a19b Mon Sep 17 00:00:00 2001 From: Achim Gsell Date: Fri, 18 Sep 2015 18:24:02 +0200 Subject: [PATCH] Bootstrap: - new command 'modbuild' to build modules - separate bootstrapping from other build-blocks - review BASH libraries - adapt build-blocks in bootstrapping --- Modules/build | 10 +- Pmodules/libpbuild.bash | 904 ++++++++++++++++++ Pmodules/libpmodules.bash | 115 --- Pmodules/libstd.bash | 141 +++ Pmodules/modbuild | 105 ++ Pmodules/modmanage.bash.in | 1 + Pmodules/modulecmd.bash.in | 1 + Tcl/build | 12 +- bash/build | 8 +- compile_pmodules.sh | 13 +- Pmodules_version.conf => config/versions.conf | 0 coreutils/build | 8 +- dialog/build | 8 +- getopt/build | 14 +- gettext/build | 8 +- install_pmodules.sh | 20 +- 16 files changed, 1202 insertions(+), 166 deletions(-) create mode 100644 Pmodules/libpbuild.bash create mode 100644 Pmodules/libstd.bash create mode 100755 Pmodules/modbuild rename Pmodules_version.conf => config/versions.conf (100%) diff --git a/Modules/build b/Modules/build index c2cb49c..410fc78 100755 --- a/Modules/build +++ b/Modules/build @@ -1,10 +1,10 @@ -#!/usr/bin/env pbuild +#!/usr/bin/env modbuild TCL_DIR="${PMODULES_ROOT}/Tools/Pmodules/${PMODULES_VERSION}" PATH="${TCL_DIR}/bin:${PATH}" -pmodules.configure() { +pbuild::configure() { case ${OS} in Linux ) declare -x LIBS="-lz -lpthread" @@ -23,7 +23,7 @@ pmodules.configure() { || exit 1 } -pmodules.post_install() { +pbuild::post_install() { rm -v ${PREFIX}/Modules/bin/add.modules rm -v ${PREFIX}/Modules/bin/mkroot rm -rfv ${PREFIX}/Modules/modulefiles @@ -45,8 +45,8 @@ module() { # use system gcc to compile declare -rx CC=gcc -pmodules.add_to_group 'Tools' -pmodules.make_all +pbuild::add_to_group 'Tools' +pbuild::make_all # Local Variables: # mode: sh diff --git a/Pmodules/libpbuild.bash b/Pmodules/libpbuild.bash new file mode 100644 index 0000000..3a12d3f --- /dev/null +++ b/Pmodules/libpbuild.bash @@ -0,0 +1,904 @@ +#!/bin/bash + +# disable auto-echo feature of 'cd' +unset CDPATH + +shopt -s expand_aliases +declare -rx ARGS="$@" + +if [[ -z ${BUILD_BLOCK} ]]; then + # legacy support for 'old-style' build-blocks + declare -r BUILD_BLOCK_DIR=$( cd $(dirname "$0") && pwd ) + declare -r BUILD_BLOCK="${BUILD_BLOCK_DIR}"/$(basename "$0") +fi + + + +PATH=/usr/bin:/bin:/usr/sbin:/sbin + +declare -r OS=$(uname -s) +if [[ "${OS}" == "Darwin" ]]; then + # :FIXME: why do we need this? + [[ -d "/opt/X11/bin" ]] && PATH+=':/opt/X11/bin' +fi + + +# number of parallel make jobs +declare -i JOBS=3 + +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" + +#source "${BUILD_CONFIGDIR}/Pmodules.conf" + +declare -x PREFIX='' +declare -x DOCDIR='' +declare -x MODULE_GROUP='' +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 + +############################################################################## +# +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 +} + +############################################################################## +# +# test whether a the string passed in $1 is a valid release name +# +# $1: string to be tested +# +is_release () { + [[ ${releases} =~ :$1: ]] +} + +############################################################################## +# +# set supported OS +# +# $1: OS (as printed by 'uname -s') +# +pbuild::supported_os() { + for os in "$@"; do + [[ ${os} == ${OS} ]] && return 0 + done + std::die 0 "${P}: Not available for ${OS}." +} + +############################################################################## +# +# install module in given group +# +# $1: group +# +pbuild::add_to_group() { + if [[ -z ${1} ]]; then + std::die 42 "${FUNCNAME}: Missing group argument." + fi + if [[ ! -d ${PMODULES_ROOT}/${PMODULES_TEMPLATES_DIR}/${1} ]]; then + std::die 43 "${1}: group does not exist." + fi + MODULE_GROUP=$1 +} + +############################################################################## +# +# set build-/runtime dependencies +# +# $@: dependencies +# +pbuild::set_build_dependencies() { + MODULE_BUILD_DEPENDENCIES=("$@") +} + +pbuild::set_runtime_dependencies() { + MODULE_DEPENDENCIES=("$@") +} + +############################################################################## +# +# set documentation file to be installed +# +# $@: documentation files relative to source +# +pbuild::set_docfiles() { + MODULE_DOCFILES=("$@") +} + +############################################################################## +# +# set supported compilers +# +# $@: compilers +# +pbuild::set_supported_compilers() { + MODULE_SUPPORTED_COMPILERS=("$@") +} + +############################################################################## +# +# test availablity of a module +# +# $@: module +# +module_is_available() { + [[ -n $("${MODULECMD}" bash avail "$1" 2>&1 1>/dev/null) ]] +} + +############################################################################## +# +# test whether a module is loaded or not +# +# $1: module name +# +is_loaded() { + [[ :${LOADEDMODULES}: =~ :$1: ]] +} + + +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 + is_loaded "$m" && continue + if ! module_is_available "$m"; then + std::debug "${m}: module not available" + local rels=( ${releases//:/ } ) + for rel in "${rels[@]}"; do + std::debug "${m}: check release \"${rel}\"" + eval $("${MODULECMD}" bash use ${rel}) + if module_is_available "${m}"; then + std::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 ]] && { + std::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 + std::die 1 "$m: oops: build failed..." + fi + fi + # :FIXME: this doesn't work any more! + local modulepath_root="${PMODULES_ROOT}/${PMODULES_MODULEFILES_DIR}" + 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 + load_build_dependencies() { + : + } +fi + +write_runtime_dependencies() { + local -r fname="${PREFIX}/.dependencies" + std::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 +} + +write_build_dependencies() { + local -r fname="${PREFIX}/.build_dependencies" + std::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 +} + +############################################################################## +# +# cleanup environment +# +pbuild::cleanup_env() { + + C_INCLUDE_PATH='' + CPLUS_INCLUDE_PATH='' + CPP_INCLUDE_PATH='' + LIBRARY_PATH='' + LD_LIBRARY_PATH='' + DYLD_LIBRARY_PATH='' + + CFLAGS='' + CPPFLAGS='' + CXXFLAGS='' + LIBS='' + LDFLAGS='' +} + +############################################################################## +# +# load default versions +# + _setup_env1() { + local varname='' + while read _name _version; do + [[ -z ${_name} ]] && continue + [[ -z ${_version} ]] && continue + [[ "${_name:0:1}" == '#' ]] && continue + var_name=$(echo ${_name} | tr [:lower:] [:upper:])_VERSION + # don't set version, if already set + if [[ -z ${!var_name} ]]; then + eval ${var_name}="${_version}" + fi + done < "${BUILD_VERSIONSFILE}" + +} + +# +# find tarball +# $1: download directory +# $2: name without version +# $3: version +# +find_tarball() { + local -r dir=$1 + local -r name=$2 + local -r version=$3 + + TARBALL="" + local ext + for ext in tar tar.gz tgz tar.bz2 tar.xz; do + local fname="${dir}/${name}-${OS}-${version}.${ext}" + if [[ -r "${fname}" ]]; then + TARBALL="${fname}" + break + fi + local fname="${dir}/${name}-${version}.${ext}" + if [[ -r "${fname}" ]]; then + TARBALL="${fname}" + break + fi + done + if [[ -z ${TARBALL} ]]; then + std::error "tar-ball for $P/$V not found." + exit 43 + fi +} + +#setup module specific environment +setup_env2() { + if [[ -z ${MODULE_GROUP} ]]; then + std::die 1 "$P: group 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 + std::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_GROUP} 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}" + ;; + OPAL ) + MODULE_RPREFIX="${P}/${V}" + MODULE_RPREFIX+="/${OPAL}/${OPAL_VERSION}" + MODULE_RPREFIX+="/${MPI}/${MPI_VERSION}" + MODULE_RPREFIX+="/${COMPILER}/${COMPILER_VERSION}" + + MODULE_NAME="${COMPILER}/${COMPILER_VERSION}/" + MODULE_NAME+="${MPI}/${MPI_VERSION}/" + MODULE_NAME+="${OPAL}/${OPAL_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_SERIAL_VERSION}/" + MODULE_NAME+="${P}/${V}" + ;; + * ) + std::die 1 "$P: oops: unknown group: ${MODULE_GROUP}" + ;; + esac + + # set PREFIX of module + PREFIX="${PMODULES_ROOT}/${MODULE_GROUP}/${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} + std::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' + std::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' + std::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' + std::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 + find_tarball "${BUILD_DOWNLOADSDIR}" "${P/_serial}" "${V}" + +} + +# redefine function for bootstrapping +setup_env2_bootstrap() { + if [[ -z ${MODULE_GROUP} ]]; then + std::die 1 "$P: group not set." + fi + + if [[ -z $V ]]; then + V=$(eval echo \$${_P}_VERSION) + fi + + # oops, we need a version + if [[ -z $V ]]; then + std::die 1 "$P: Missing version." + fi + MODULE_SRCDIR="${BUILD_TMPDIR}/src/${P/_serial}-$V" + MODULE_BUILDDIR="${BUILD_TMPDIR}/build/$P-$V" + MODULE_GROUP='Tools' + MODULE_NAME="Pmodules/${PMODULES_VERSION}" + # set PREFIX of module + PREFIX="${PMODULES_ROOT}/${MODULE_GROUP}/${MODULE_NAME}" + + MODULE_RELEASE='unstable' + std::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 + find_tarball "${BUILD_DOWNLOADSDIR}" "${P/_serial}" "${V}" + + 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" +} + +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}" + +} + +pbuild::pre_configure() { + : +} + +pbuild::configure() { + ${MODULE_SRCDIR}/configure \ + --prefix="${PREFIX}" +} + +pbuild::build() { + make -j${JOBS} +} + +pbuild::install() { + make install +} + +pbuild::post_install() { + : +} + +pbuild::install_doc() { + std::info "Installing documentation to ${DOCDIR}" + install -m 0755 -d "${DOCDIR}" + install -m0444 "${MODULE_DOCFILES[@]/#/${MODULE_SRCDIR}/}" "${BUILD_BLOCK}" "${DOCDIR}" +} + +set_legacy_link() { + local -r link_name="${PMODULES_ROOT}/${PMODULES_MODULEFILES_DIR}/${MODULE_GROUP}/${MODULE_NAME}" + local -r dir_name=${link_name%/*} + local -r release_file="${dir_name}/.release-${MODULE_NAME##*/}" + if [[ ! -e "${_path}" ]]; then + ( + std::info "Setting new sym-link \"${link_name}\" ..." + mkdir -p "${dir_name}" + cd "${dir_name}" + local x + IFS='/' x=( ${dir_name/${PMODULES_ROOT}\/${PMODULES_MODULEFILES_DIR}\/} ) + local n=${#x[@]} + local -r _target="../"$(eval printf "../%.s" {1..${n}})${PMODULES_TEMPLATES_DIR##*/}/"${MODULE_GROUP}/${P}/modulefile" + ln -fs "${_target}" "${MODULE_NAME##*/}" + ) + fi + std::info "${MODULE_NAME}: set release to '${MODULE_RELEASE}'" + echo "${MODULE_RELEASE}" > "${release_file}" +} + +set_link() { + local -r link_name="${PMODULES_ROOT}/${MODULE_GROUP}/${PMODULES_MODULEFILES_DIR}/${MODULE_NAME}" + local -r dir_name=${link_name%/*} + local -r release_file="${dir_name}/.release-${MODULE_NAME##*/}" + if [[ ! -e "${_path}" ]]; then + ( + std::info "Setting new sym-link \"${link_name}\" ..." + mkdir -p "${dir_name}" + cd "${dir_name}" + local x + IFS='/' x=( ${dir_name/${PMODULES_ROOT}\/${MODULE_GROUP}\/} ) + local n=${#x[@]} + local -r _target="../"$(eval printf "../%.s" {1..${n}})${PMODULES_TEMPLATES_DIR##*/}/"${MODULE_GROUP}/${P}/modulefile" + ln -fs "${_target}" "${MODULE_NAME##*/}" + ) + fi + std::info "${MODULE_NAME}: set release to '${MODULE_RELEASE}'" + echo "${MODULE_RELEASE}" > "${release_file}" +} + +pbuild::cleanup_build() { + [[ -n "${MODULE_BUILDDIR}" ]] \ + || std::die 1 "Oops: internal error: MODULE_BUILDDIR is set to empty string..." + [[ "${MODULE_BUILDDIR}" == "/" ]] \ + && std::die 1 "Oops: internal error: MODULE_BUILDDIR is set to '/'..." + [[ -d "/${MODULE_BUILDDIR}" ]] \ + || std::die 1 "Oops: internal error: MODULE_BUILDDIR=${MODULE_BUILDDIR} is not a directory..." + echo "Cleaning up '/${MODULE_BUILDDIR}'..." + rm -rf "/${MODULE_BUILDDIR}" +} + +pbuild::cleanup_src() { + ( + [[ -d /${MODULE_SRCDIR} ]] || return 0 + cd "/${MODULE_SRCDIR}/.."; + if [[ $(pwd) != / ]]; then + echo "Cleaning up $(pwd)" + rm -rf ${MODULE_SRCDIR##*/} + fi + ); + return 0 +} + +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 + std::die 0 "Package cannot be build with ${COMPILER}/${COMPILER_VERSION}." +} + + +# unfortunatelly sometime we need an OS depended post-install +post_install_linux() { + cd "${PREFIX}" + # solve multilib problem with LIBRARY_PATH on 64bit Linux + [[ -d "lib" ]] && [[ ! -d "lib64" ]] && ln -s lib lib64 + return 0 +} + +post_install() { + std::info "Run post-installation for ${OS} ..." + [[ "${OS}" == "Linux" ]] && post_install_linux + std::info "Post-installation done ..." + return 0 +} + +pbuild::make_all() { + local building='no' + echo "${P}:" + _setup_env1 + load_build_dependencies + # setup module specific environment + if [[ ${bootstrap} == no ]]; then + setup_env2 + else + setup_env2_bootstrap + fi + + if [[ ! -d "${PREFIX}" ]] || [[ ${force_rebuild} == 'yes' ]] || [[ ${bootstrap} == 'yes' ]]; then + building='yes' + echo "Building $P/$V ..." + [[ ${dry_run} == yes ]] && std::die 0 "" + check_compiler + + if [[ ! -e "${MODULE_BUILDDIR}/.prep" ]]; then + prep + touch "${MODULE_BUILDDIR}/.prep" + fi + [[ "${target}" == "prep" ]] && return 0 + + if [[ ! -e "${MODULE_BUILDDIR}/.configure" ]]; then + cd "${MODULE_SRCDIR}" + pbuild::pre_configure + cd "${MODULE_BUILDDIR}" + pbuild::configure + touch "${MODULE_BUILDDIR}/.configure" + fi + [[ "${target}" == "configure" ]] && return 0 + + if [[ ! -e "${MODULE_BUILDDIR}/.compile" ]]; then + cd "${MODULE_BUILDDIR}" + pbuild::build + touch "${MODULE_BUILDDIR}/.compile" + fi + [[ "${target}" == "compile" ]] && return 0 + + if [[ ! -e "${MODULE_BUILDDIR}/.install" ]]; then + cd "${MODULE_BUILDDIR}" + pbuild::install + pbuild::post_install + if typeset -F pbuild::post_install_${OS} 1>/dev/null 2>&1; then + pbuild::post_install_${OS} "$@" + fi + pbuild::install_doc + post_install + if [[ ${bootstrap} == 'no' ]]; then + write_runtime_dependencies + write_build_dependencies + fi + touch "${MODULE_BUILDDIR}/.install" + fi + [[ "${target}" == "install" ]] && return 0 + + [[ ${enable_cleanup_build} == yes ]] && pbuild::cleanup_build + [[ ${enable_cleanup_src} == yes ]] && pbuild::cleanup_src + + else + echo "Not rebuilding $P/$V ..." + fi + if [[ ${bootstrap} == 'no' ]]; then + if [[ -d "${PMODULES_ROOT}/${PMODULES_MODULEFILES_DIR}" ]]; then + set_legacy_link + fi + set_link + fi + return 0 +} + +############################################################################## +# +debug_on='no' +force_rebuild='no' +ENVIRONMENT_ARGS='' +dry_run='no' +enable_cleanup_build='yes' +enable_cleanup_src='no' + +target='all' + +pbuild::cleanup_env + +# 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 + ;; + --disable-cleanup ) + enable_cleanup_build='no' + enable_cleanup_src='no' + ;; + --enable-cleanup-build ) + enable_cleanup_build='yes' + ;; + --disable-cleanup-build ) + enable_cleanup_build='no' + ;; + --enable-cleanup-src ) + enable_cleanup_src='yes' + ;; + --disable-cleanup-src ) + enable_cleanup_src='no' + ;; + --dry-run ) + dry_run='yes' + ;; + --release=* ) + MODULE_RELEASE=${1/--release=} + ;; + --with=*/* ) + with_modules+=( ${1/--with=} ) + ;; + *=* ) + eval $1 + ENVIRONMENT_ARGS="${ENVIRONMENT_ARGS} $1" + ;; + prep | configure | compile | install | all ) + target=$1 + ;; + [0-9]* ) + V=$1 + ;; + * ) + usage + ;; + 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 + if [[ -r "${BUILD_BLOCK_DIR}/with_modules-$V" ]]; then + with_modules+=( $(cat "${BUILD_BLOCK_DIR}/with_modules-$V") ) + elif [[ -r "${BUILD_BLOCK_DIR}/with_modules" ]]; then + with_modules+=( $(cat "${BUILD_BLOCK_DIR}/with_modules") ) + fi + + source "${PMODULES_ROOT}/${PMODULES_CONFIG_DIR}/profile.bash" + MODULECMD="${PMODULES_HOME}/bin/modulecmd" + [[ -x ${MODULECMD} ]] || std::die 1 "${MODULECMD}: no such executable" + module purge + module use unstable + # :FIXME: this is a hack!!! + module use Libraries + for m in "${with_modules[@]}"; do + if module_is_available "$m"; then + echo "Loading module: ${m}" + module load "${m}" + else + std::die 44 "$m: module not available!" + fi + done +else + unset PMODULES_HOME + unset PMODULES_VERSION + std::read_versions "${BUILD_BASEDIR}/config/versions.conf" +fi + +P=$(basename $(dirname "${BUILD_BLOCK}")) +P=${P%.*} +_P=$(echo $P | tr [:lower:] [:upper:]) +_P=${_P//-/_} +_V=${_P}_VERSION + +eval "${ENVIRONMENT_ARGS}" + +if [[ -n ${PMODULES_DEFINED_RELEASES} ]]; then + declare -r releases="${PMODULES_DEFINED_RELEASES}" +else + # set defaults, if file doesn't exist or isn't readable + declare -r releases=":unstable:stable:deprecated:" +fi + +# Local Variables: +# mode: sh +# sh-basic-offset: 8 +# tab-width: 8 +# End: diff --git a/Pmodules/libpmodules.bash b/Pmodules/libpmodules.bash index 9e7d27e..db19b96 100644 --- a/Pmodules/libpmodules.bash +++ b/Pmodules/libpmodules.bash @@ -35,121 +35,6 @@ pmodules::check_env() { } -std::log() { - local -ri fd=$1 - local -r fmt="$2\n" - shift 2 - printf -- "$fmt" "$@" 1>&$fd -} - -std::info() { - std::log 2 "$1" "${@:2}" -} - -std::error() { - std::log 2 "$1" "${@:2}" -} - -std::debug() { - [[ ${PMODULES_DEBUG} ]] || return 0 - std::log 2 "$@" -} - -std::die() { - local -ri ec=$1 - shift - if [[ -n $@ ]]; then - local -r fmt=$1 - shift - std::log 2 "$fmt" "$@" - fi - exit $ec -} - -# -# get answer to yes/no question -# -# $1: prompt -# -std::get_YN_answer() { - local -r prompt="$1" - local ans - read -p "${prompt}" ans - case ${ans} in - y|Y ) - return 0;; - * ) - return 1;; - esac -} - -# -# return normalized abolute pathname -# $1: filename -std::get_abspath() { - local -r fname=$1 - [[ -r "${fname}" ]] || return 1 - if [[ -d ${fname} ]]; then - echo $(cd "${fname}" && pwd) - else - local -r dname=$(dirname "${fname}") - echo $(cd "${dname}" && pwd)/$(basename "${fname}") - fi -} - -std::append_path () { - local -r P=$1 - local -r d=$2 - - if ! echo ${!P} | egrep -q "(^|:)${d}($|:)" ; then - if [[ -z ${!P} ]]; then - eval $P=${d} - else - eval $P=${!P}:${d} - fi - fi -} - -std::prepend_path () { - local -r P=$1 - local -r d=$2 - - if ! echo ${!P} | egrep -q "(^|:)${d}($|:)" ; then - if [[ -z ${!P} ]]; then - eval $P=${d} - else - eval $P=${d}:${!P} - fi - fi -} - -std::remove_path() { - local -r P=$1 - local -r d=$2 - local new_path='' - local -r _P=( ${!P//:/ } ) - # loop over all entries in path - for entry in "${_P[@]}"; do - [[ "${entry}" != "${d}" ]] && new_path+=":${entry}" - done - # remove leading ':' - eval ${P}="${new_path:1}" -} - -# -# split file name -# -std::split_fname() { - local -r savedIFS="${IFS}" - IFS='/' - local std__split_fname_result__=( $(echo "${@: -1}") ) - IFS=${savedIFS} - eval $1=\(\"\${std__split_fname_result__[@]}\"\) - if (( $# >= 3 )); then - eval $2=${#std__split_fname_result__[@]} - fi -} - # Local Variables: # mode: sh # sh-basic-offset: 8 diff --git a/Pmodules/libstd.bash b/Pmodules/libstd.bash new file mode 100644 index 0000000..3f142bb --- /dev/null +++ b/Pmodules/libstd.bash @@ -0,0 +1,141 @@ +#!/bin/bash + +# +# logging/message functions +# +std::log() { + local -ri fd=$1 + local -r fmt="$2\n" + shift 2 + printf -- "$fmt" "$@" 1>&$fd +} + +std::info() { + std::log 2 "$1" "${@:2}" +} + +std::error() { + std::log 2 "$1" "${@:2}" +} + +std::debug() { + [[ ${PMODULES_DEBUG} ]] || return 0 + std::log 2 "$@" +} + +std::die() { + local -ri ec=$1 + shift + if [[ -n $@ ]]; then + local -r fmt=$1 + shift + std::log 2 "$fmt" "$@" + fi + exit $ec +} + +# +# get answer to yes/no question +# +# $1: prompt +# +std::get_YN_answer() { + local -r prompt="$1" + local ans + read -p "${prompt}" ans + case ${ans} in + y|Y ) + return 0;; + * ) + return 1;; + esac +} + +# +# return normalized abolute pathname +# $1: filename +std::get_abspath() { + local -r fname=$1 + [[ -r "${fname}" ]] || return 1 + if [[ -d ${fname} ]]; then + echo $(cd "${fname}" && pwd) + else + local -r dname=$(dirname "${fname}") + echo $(cd "${dname}" && pwd)/$(basename "${fname}") + fi +} + +std::append_path () { + local -r P=$1 + local -r d=$2 + + if ! echo ${!P} | egrep -q "(^|:)${d}($|:)" ; then + if [[ -z ${!P} ]]; then + eval $P=${d} + else + eval $P=${!P}:${d} + fi + fi +} + +std::prepend_path () { + local -r P=$1 + local -r d=$2 + + if ! echo ${!P} | egrep -q "(^|:)${d}($|:)" ; then + if [[ -z ${!P} ]]; then + eval $P=${d} + else + eval $P=${d}:${!P} + fi + fi +} + +std::remove_path() { + local -r P=$1 + local -r d=$2 + local new_path='' + local -r _P=( ${!P//:/ } ) + # loop over all entries in path + for entry in "${_P[@]}"; do + [[ "${entry}" != "${d}" ]] && new_path+=":${entry}" + done + # remove leading ':' + eval ${P}="${new_path:1}" +} + +# +# split file name +# +std::split_fname() { + local -r savedIFS="${IFS}" + IFS='/' + local std__split_fname_result__=( $(echo "${@: -1}") ) + IFS=${savedIFS} + eval $1=\(\"\${std__split_fname_result__[@]}\"\) + if (( $# >= 3 )); then + eval $2=${#std__split_fname_result__[@]} + fi +} + +std::read_versions() { + local -r fname="$1" + local varname='' + while read _name _version; do + [[ -z ${_name} ]] && continue + [[ -z ${_version} ]] && continue + [[ "${_name:0:1}" == '#' ]] && continue + var_name=$(echo ${_name} | tr [:lower:] [:upper:])_VERSION + # don't set version, if already set + if [[ -z ${!var_name} ]]; then + eval ${var_name}="${_version}" + fi + done < "${fname}" +} + + +# Local Variables: +# mode: sh +# sh-basic-offset: 8 +# tab-width: 8 +# End: diff --git a/Pmodules/modbuild b/Pmodules/modbuild new file mode 100755 index 0000000..a029ffd --- /dev/null +++ b/Pmodules/modbuild @@ -0,0 +1,105 @@ +#!/bin/bash + +set -x + +declare -r mydir=$(dirname "$0") +declare -r libpbuild='libpbuild.bash' +declare -r libstd='libstd.bash' +declare -r pmodule_environment='environment.bash' + +# source BASH library with standard functions +if [[ -r ${mydir}/${libstd} ]]; then + source "${mydir}/${libstd}" +elif [[ -r ${mydir}/../lib/${libstd} ]]; then + source "${mydir}/../lib/${libstd}" +else + echo "Oops: required BASH library '${libstd}' not found" 1>&2 + exit 1 +fi + +declare -rx BUILD_BLOCK=$(std::get_abspath "$1") +declare -rx BUILD_BLOCK_DIR=$(dirname "${BUILD_BLOCK}") +shift 1 + +# source Pmodule environment configuration +if [[ -r ${mydir}/../config/${pmodule_environment} ]]; then + # we are bootstrapping + source "${mydir}/../config/${pmodule_environment}" + declare -rx BUILD_BASEDIR=$(std::get_abspath "${mydir}/..") + bootstrap='yes' + +elif [[ -n ${PMODULES_ROOT} ]] && [[ -n ${PMODULES_CONFIG_DIR} ]] && \ + [[ -r ${PMODULES_ROOT}/${PMODULES_CONFIG_DIR}/${pmodule_environment} ]]; then + source ${PMODULES_ROOT}/${PMODULES_CONFIG_DIR}/${pmodule_environment} + declare -rx BUILD_BASEDIR=$(std::get_abspath "${BUILD_BLOCK_DIR}/../../..") + bootstrap='no' + +fi + +# +# 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 gdate 2>/dev/null) +if [[ $__path ]]; then + alias date=$__path +else + alias date=$(which date 2>/dev/null) +fi + +__path=$(which ginstall 2>/dev/null) +if [[ $__path ]]; then + alias install=$__path +else + alias install=$(which install 2>/dev/null) +fi + +__path=$(which greadlink 2>/dev/null) +if [[ $__path ]]; then + alias readlink=$__path +else + alias readlink=$(which readlink 2>/dev/null) +fi + +__path=$(which gsed 2>/dev/null) +if [[ $__path ]]; then + alias sed=$__path +else + alias sed=$(which sed 2>/dev/null) +fi + +# +# set an error handler. If a function _exit() exists, it will be called +# with the passed exit code. +# +# $1 exit code +# +set -o errexit + +trap "std::error_handler" ERR + +std::error_handler() { + local -i ec=$? + + typeset -F _exit 1>/dev/null 2>&1 && _exit "${ec}" + exit ${ec} +} + +# +# run build +# + +if [[ -r ${mydir}/${libpbuild} ]]; then + source "${mydir}/${libpbuild}" +elif [[ -r ${mydir}/../lib/${libpbuild} ]]; then + source "${mydir}/../lib/${libpbuild}" +else + echo "Oops: required BASH library '${libpbuild}' not found" 1>&2 + exit 1 +fi + +source "${BUILD_BLOCK}" diff --git a/Pmodules/modmanage.bash.in b/Pmodules/modmanage.bash.in index 87ed047..015fd48 100755 --- a/Pmodules/modmanage.bash.in +++ b/Pmodules/modmanage.bash.in @@ -12,6 +12,7 @@ declare -r bindir="${prefix}/bin" declare -r libdir="${prefix}/lib" declare -r libexecdir="${prefix}/libexec" +source "${libdir}/libstd.bash" source "${libdir}/libpmodules.bash" PATH="${bindir}:${PATH}" diff --git a/Pmodules/modulecmd.bash.in b/Pmodules/modulecmd.bash.in index 918b751..adf9475 100755 --- a/Pmodules/modulecmd.bash.in +++ b/Pmodules/modulecmd.bash.in @@ -14,6 +14,7 @@ declare -r bindir="${prefix}/bin" declare -r libdir="${prefix}/lib" declare -r libexecdir="${prefix}/libexec" +source "${libdir}/libstd.bash" source "${libdir}/libpmodules.bash" PATH="${bindir}:${PATH}" diff --git a/Tcl/build b/Tcl/build index 379122e..2f7bab6 100755 --- a/Tcl/build +++ b/Tcl/build @@ -1,6 +1,6 @@ -#!/usr/bin/env pbuild +#!/usr/bin/env modbuild -pmodules.configure() { +pbuild::configure() { case ${OS} in Linux ) srcdir="${MODULE_SRCDIR}/unix" @@ -15,10 +15,10 @@ pmodules.configure() { || exit 1 } -pmodules.post_install() { +pbuild::post_install() { { cd "${PREFIX}"/bin && rm -f tclsh && ln -fs tclsh${V%.*} tclsh; }; } -pmodules.add_to_group 'Tools' -pmodules.set_docfiles 'license.terms' 'README' -pmodules.make_all +pbuild::add_to_group 'Tools' +pbuild::set_docfiles 'license.terms' 'README' +pbuild::make_all diff --git a/bash/build b/bash/build index 70cdb1a..27e258a 100755 --- a/bash/build +++ b/bash/build @@ -1,10 +1,10 @@ -#!/usr/bin/env pbuild +#!/usr/bin/env modbuild -pmodules.configure() { +pbuild::configure() { "${MODULE_SRCDIR}"/configure \ --prefix="${PREFIX}" \ || exit 1 } -pmodules.add_to_group 'Tools' -pmodules.make_all +pbuild::add_to_group 'Tools' +pbuild::make_all diff --git a/compile_pmodules.sh b/compile_pmodules.sh index 00fcdb2..b8181ed 100755 --- a/compile_pmodules.sh +++ b/compile_pmodules.sh @@ -1,15 +1,14 @@ #!/bin/bash -declare -r BASE_DIR=$(cd "$(dirname $0)/.." && pwd) -declare -r BOOTSTRAP_DIR="${BASE_DIR}/Bootstrap" - -source "${BASE_DIR}/lib/lib.bash" +declare -r BOOTSTRAP_DIR=$(dirname "$0") unset PMODULES_HOME unset PMODULES_VERSION -read_versions "${BOOTSTRAP_DIR}/Pmodules_version.conf" -source "/opt/psi/config/environment.bash" +source "${BOOTSTRAP_DIR}/Pmodules/libstd.bash" +source "${BOOTSTRAP_DIR}/config/environment.bash" + +std::read_versions "${BOOTSTRAP_DIR}/config/versions.conf" #if [[ -n ${PMODULES_DIR} ]] && [[ "${PMODULES_DIR}" != "/" ]] && [[ -d "${PMODULES_DIR}" ]]; then # rm -rf "${PMODULES_DIR}" @@ -19,7 +18,7 @@ build () { local -r name="$1" local -r version="$2" - "${BOOTSTRAP_DIR}/${name}/build" --bootstrap --disable-cleanup "${version}" || \ + "${BOOTSTRAP_DIR}/Pmodules/modbuild" "${BOOTSTRAP_DIR}/${name}/build" --disable-cleanup "${version}" || \ std::die 3 "Compiling '${name}' failed!" } diff --git a/Pmodules_version.conf b/config/versions.conf similarity index 100% rename from Pmodules_version.conf rename to config/versions.conf diff --git a/coreutils/build b/coreutils/build index 63030c7..ed5e87a 100755 --- a/coreutils/build +++ b/coreutils/build @@ -1,10 +1,10 @@ -#!/usr/bin/env pbuild +#!/usr/bin/env modbuild -pmodules.configure() { +pbuild::configure() { "${MODULE_SRCDIR}"/configure \ --prefix="${PREFIX}" \ || exit 1 } -pmodules.add_to_group 'Tools' -pmodules.make_all +pbuild::add_to_group 'Tools' +pbuild::make_all diff --git a/dialog/build b/dialog/build index 70cdb1a..27e258a 100755 --- a/dialog/build +++ b/dialog/build @@ -1,10 +1,10 @@ -#!/usr/bin/env pbuild +#!/usr/bin/env modbuild -pmodules.configure() { +pbuild::configure() { "${MODULE_SRCDIR}"/configure \ --prefix="${PREFIX}" \ || exit 1 } -pmodules.add_to_group 'Tools' -pmodules.make_all +pbuild::add_to_group 'Tools' +pbuild::make_all diff --git a/getopt/build b/getopt/build index b7984cb..d486ed3 100755 --- a/getopt/build +++ b/getopt/build @@ -1,10 +1,10 @@ -#!/usr/bin/env pbuild +#!/usr/bin/env modbuild -pmodules.configure() { +pbuild::configure() { : } -pmodules.build() { +pbuild::build() { case ${OS} in Linux ) declare -x LDFLAGS="-lintl" @@ -18,20 +18,20 @@ pmodules.build() { make -e } -pmodules.install() { +pbuild::install() { cd "${MODULE_SRCDIR}" declare -x DESTDIR="${PREFIX}" declare -x prefix='' make -e install } -pmodules.cleanup_build() { +pbuild::cleanup_build() { cd "${MODULE_SRCDIR}" make -e realclean } -pmodules.add_to_group 'Tools' -pmodules.make_all +pbuild::add_to_group 'Tools' +pbuild::make_all # Local Variables: # mode: sh diff --git a/gettext/build b/gettext/build index ad631df..c859d43 100755 --- a/gettext/build +++ b/gettext/build @@ -1,6 +1,6 @@ -#!/usr/bin/env pbuild +#!/usr/bin/env modbuild -pmodules.configure() { +pbuild::configure() { "${MODULE_SRCDIR}"/configure \ --prefix="${PREFIX}" \ --disable-java \ @@ -18,5 +18,5 @@ pmodules.configure() { || exit 1 } -pmodules.add_to_group 'Tools' -pmodules.make_all +pbuild::add_to_group 'Tools' +pbuild::make_all diff --git a/install_pmodules.sh b/install_pmodules.sh index d6965c4..489625a 100755 --- a/install_pmodules.sh +++ b/install_pmodules.sh @@ -1,16 +1,13 @@ #!/bin/bash +declare BOOTSTRAP_DIR=$(dirname "$0") +source "${BOOTSTRAP_DIR}/Pmodules/libstd.bash" +source "${BOOTSTRAP_DIR}/config/environment.bash" -declare -r BASE_DIR=$(cd "$(dirname $0)/.." && pwd) -declare -r BOOTSTRAP_DIR="${BASE_DIR}/Bootstrap" +declare -r BOOTSTRAP_DIR=$(std::get_abspath "${BOOTSTRAP_DIR}") declare -r SRC_DIR="${BOOTSTRAP_DIR}/Pmodules" -source "${BASE_DIR}/lib/lib.bash" +std::read_versions "${BOOTSTRAP_DIR}/config/versions.conf" -unset PMODULES_HOME -unset PMODULES_VERSION - -read_versions "${BOOTSTRAP_DIR}/Pmodules_version.conf" -source "/opt/psi/config/environment.bash" echo "Installing to ${PMODULES_HOME} ..." sed_cmd="s:@PMODULES_HOME@:${PMODULES_HOME}:g;" @@ -30,14 +27,17 @@ install -m 0755 "${SRC_DIR}/modulecmd.bash" "${PMODULES_HOME}/libexec" install -m 0755 "${SRC_DIR}/modmanage" "${PMODULES_HOME}/bin" install -m 0755 "${SRC_DIR}/modmanage.bash" "${PMODULES_HOME}/libexec" install -m 0755 "${SRC_DIR}/dialog.bash" "${PMODULES_HOME}/bin" +install -m 0755 "${SRC_DIR}/modbuild" "${PMODULES_HOME}/bin" -install -m 0755 "${SRC_DIR}/environment.bash" "${PMODULES_HOME}/config" -install -m 0755 "${SRC_DIR}/profile.bash" "${PMODULES_HOME}/config" +install -m 0755 "${SRC_DIR}/environment.bash" "${PMODULES_HOME}/config/environment.bash.in" +install -m 0755 "${SRC_DIR}/profile.bash" "${PMODULES_HOME}/config/profile.bash.in" install -m 0644 "${SRC_DIR}/bash" "${PMODULES_HOME}/init" install -m 0644 "${SRC_DIR}/bash_completion" "${PMODULES_HOME}/init" install -m 0644 "${SRC_DIR}/libpmodules.bash" "${PMODULES_HOME}/lib" +install -m 0644 "${SRC_DIR}/libpbuild.bash" "${PMODULES_HOME}/lib" +install -m 0644 "${SRC_DIR}/libstd.bash" "${PMODULES_HOME}/lib" install -m 0644 "${SRC_DIR}/libmodules.tcl" "${PMODULES_HOME}/lib/tcl8.6" {