#!@BASH@ # # The following build specific variables are set and used in libpbuild.bash: # ARGS # BUILD_SCRIPT # BUILDBLOCK_DIR # #............................................................................. declare -r VERSION='@PMODULES_VERSION@' unset CDPATH # unset CDPATH, otherwise 'cd' prints the directoy! unset IFS # use default IFS #set -e # exit on error set -o pipefail set -o nounset shopt -s nocaseglob shopt -s extglob shopt -s nullglob # get absolute path of script mydir=$(cd "$(/usr/bin/dirname "$0")" && pwd -P) prefix=$(/usr/bin/dirname "${mydir}") PATH="${prefix}/bin:${prefix}/libexec:/bin:/usr/bin:/sbin:/usr/sbin" source "${prefix}/lib/libstd.bash" || { echo "Oops: cannot source library -- '$_'" 1>&2; exit 3; } source "${prefix}/lib/libpmodules.bash" || \ std::die 3 "Oops: cannot source library -- '$_'" source "${prefix}/lib/libpbuild.bash" || \ std::die 3 "Oops: cannot source library -- '$_'" unset mydir unset prefix ############################################################################## # usage() { std::error " USAGE: $0 [options..] [build_script] version MANDATORY ARGUMENTS: version Version of module to build. SELECT VARIANT TO BUILD: --system Specify the system for selecting a variants. Defaults to the OS version and release like 'rhel6'. --with=P/V Select variant to compile. Use multiple '--with' arguments to make the selected variant unique. BUILD-STEPS OPTIONS: --clean-install Remove module if already exist before building. --prep Prepare sources: unpack sources and apply patches only. --configure Prepare and configure sources. --compile Prepare, configure and compile everything. --install Prepare, configure and compile everything. Finally run install step. Do not cleanup build and source directory. --all Run throu all steps including cleanup. -update-modulefiles Only install the modulefile and set the release. MISCELLANEOUS OPTIONS: -? | -h | --help Print usage. -V | --version ) Print version. -v | --verbose ) Verbose output. --debug ) Run in debug mode. -j N | --jobs=N Run N parallel make jobs. -f | --force-rebuild Force rebuild of module. --dry-run Dry run. --disable-cleanup-build --disable-cleanup-src --disable-cleanup Disable the cleanup of files in the build and/or source directory. Default is to remove all files in the build respective source directory at the beginning of a build and after a successful build. --distdir Directory where to store and lookup downloaded files. --tmpdir Directory used for building a module. DOCUMENTATION: Full documentation is available at http://pmodules.gitpages.psi.ch " exit 1 } ############################################################################## # # parse options and arguments # # command line arguments are taken first # then configuration file # last default # save arguments, required for building dependencies declare -ra ARGS=( "$@" ) # versions to be build, '.*' or none means all declare versions_to_build='' declare opt_build_target='all' declare opt_dry_run='no' declare opt_enable_cleanup_build='yes' declare opt_enable_cleanup_src='yes' declare opt_force_rebuild='no' declare -i opt_jobs=0 declare opt_update_modulefiles='no' declare opt_system='' declare opt_verbose='no' # array collecting all modules specified on the command line via '--with=module' declare -a opt_with_modules=() declare -A opt_with_dict=() declare -- opt_config_file='' declare -- opt_debug='no' declare -- opt_check_mode='no' declare -- opt_variant='' declare -- opt_clean_install='no' declare -- opt_parent_prefix='' declare -- BUILD_SCRIPT='' declare -- yaml_config_file='' declare -- module_name='' declare -- module_type='module' declare -- echo=':' parse_args() { # # The first argument ($1) is the build-script, if called in the # usual way: # ./ [options] # If called via: # modbuild [options] # the build-script MUST be passed as first argument. # (( $# == 0 )) && usage while (( $# > 0 )); do case $1 in -j ) opt_jobs="$2" shift ;; --jobs=[0-9]* ) opt_jobs="${1/--jobs=}" ;; -v | --verbose ) trap 'echo "$BASH_COMMAND"' DEBUG opt_verbose='yes' echo='echo' ;; --debug ) opt_debug='yes' set -x ;; -f | --force-rebuild ) opt_force_rebuild='yes' ;; -\? | -h | --help ) usage ;; -V | --version ) std::die 0 "\nPmodules version ${VERSION}\nCopyright GNU GPL v2\n" ;; --dry-run ) opt_dry_run='yes' ;; --disable-cleanup ) opt_enable_cleanup_build='no' opt_enable_cleanup_src='no' ;; --disable-cleanup-build ) opt_enable_cleanup_build='no' ;; --disable-cleanup-src ) opt_enable_cleanup_src='no' ;; --distdir | --distdir=* ) if [[ $1 == *=* ]]; then PMODULES_DISTFILESDIR="${1/--distdir=}" else PMODULES_DISTFILESDIR="$2" shift fi ;; --tmpdir | --tmpdir=* ) if [[ $1 == *=* ]]; then PMODULES_TMPDIR="${1#--*=}" else PMODULES_TMPDIR="$2" shift fi ;; --system | --system=* ) if [[ $1 == *=* ]]; then opt_system="${1#--*=}" else opt_system="$2" shift fi ;; --check-mode ) opt_check_mode='yes' ;; --use-flags | --use-flags=* ) if [[ $1 == *=* ]]; then USE_FLAGS=":${1#--*=}:" else USE_FLAGS=":$2:" shift fi ;; --with | --with=*/* ) if [[ $1 == *=* ]]; then opt_with_modules+=( "${1#--*=}" ) opt_with_dict[${1#--*=}]=0 else opt_with_modules+=( "$2" ) opt_with_dict[$2]=0 shift fi ;; --prep | --configure | --compile | --install | --all ) opt_build_target=${1:2} ;; --clean-install ) opt_force_rebuild='yes' opt_clean_install='yes' ;; --update-modulefiles ) opt_update_modulefiles='yes' ;; --config-file | --config-file=* ) if [[ $1 == *=* ]]; then opt_config_file=( "${1#--*=}" ) else opt_config_file=( "$2" ) shift fi ;; --variant | --variant=* ) if [[ $1 == *=* ]]; then opt_variant=( "${1#--*=}" ) else opt_variant=( "$2" ) shift fi ;; --parent-prefix | --parent-prefix=* ) if [[ $1 == *=* ]]; then opt_parent_prefix="${1#--*=}" else opt_parent_prefix="$2" shift fi module_type='sub_package' pbuild.set_prefix "${opt_parent_prefix}" ;; -- | '' ) : ;; -* ) std::die 1 "Invalid option -- '$1'" ;; * ) local -- arg="$1" if [[ -f "${arg}" && -x "${arg}" ]]; then BUILD_SCRIPT="$(std::get_abspath "${arg}")" BUILDBLOCK_DIR=$(dirname "${BUILD_SCRIPT}") elif [[ "${arg}" == */* ]]; then module_name="${arg%/*}" versions_to_build+=" ${arg#*/}" else versions_to_build+=" $1" fi ;; esac shift done [[ -z "${BUILD_SCRIPT}" ]] && \ std::die 1 "%s " \ "Build script argument missing?" # if no version is specified on the cmd-line, build all versions #(( ${#versions_to_build[@]} > 0)) || versions_to_build+=( '.*' ) [[ -z ${versions_to_build} ]] && versions_to_build='.*' # set system if not set on the cmd-line opt_system="${opt_system:-$(std::get_os_release)}" # set config file yaml_config_file="${opt_config_file:-${BUILDBLOCK_DIR}/files/config.yaml}" [[ -r "${yaml_config_file}" ]] || \ std::die 2 \ "%s -- %s" \ "YAML config file doesn't exist or is not readable" \ "${yaml_config_file}" std::info "Using YAML configuration file - ${yaml_config_file}" } get_yaml_file_fmt(){ : " Get format version of configuration file. Print the version number to stdout if it is valid. " ${yq} -e '.' "$1" &>/dev/null || \ std::die 3 "%s -- %s" \ "Error in YAML config file, please check with linter" \ "$1" local -- fmt='' fmt=$(${yq} -e ".format" "$1") || \ std::die 3 "Error reading config file format -- $1" case "${fmt}" in 1 ) : ;; * ) std::die 3 "Unknown YAML Pmodules config file format -- $1" ;; esac echo "${fmt}" } read_yaml_config_file() { : " Test whether the configuration file '$1' provides configurations for the module '$2'. If yes, the YAML block with the configuration is printed to stdout. " local -- file_name="$1" local -- module_name="$2" local -- yaml='' yaml=$( ${yq} -Ne e ".${module_name}" "${file_name}" 2>/dev/null ) || \ std::die 3 "Configuration for '${module_name}' missing -- ${file_name}" echo "${yaml}" } build_modules(){ local -- name="$1" local -- version="$2" shift 2 local -a with_modules=( "$*" ) if [[ "${opt_check_mode}" == 'yes' ]]; then if ! which yamllint > /dev/null 2>&1; then eval $( "${modulecmd}" bash load yamllint/1.28.0 ) fi which yamllint > /dev/null 2>&1 || \ std::die 3 "yamllint not found" yamllint "${yaml_config_file}" fi local -- file_fmt='' file_fmt=$(get_yaml_file_fmt "${yaml_config_file}") local -- yaml_mod_config='' yaml_mod_config=$(read_yaml_config_file "${yaml_config_file}" "${name}") case "${file_fmt}" in 1 ) build_modules_yaml_v1 \ "${yaml_mod_config}" \ "${name}" "${version}" \ "${with_modules[@]}" ;; * ) std::die 255 "Oops!" # we should never be here! ;; esac } #...................................................................... # # Initialise environment modules. # # Arguments: # none # init_module_environment(){ 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? if [[ -e "${PMODULES_HOME%%/Tools*}/Libraries" ]]; then eval $( "${modulecmd}" bash use Libraries ) fi if [[ -e "${PMODULES_HOME%%/Tools*}/System" ]]; then eval $( "${modulecmd}" bash use System ) fi unset C_INCLUDE_PATH unset CPLUS_INCLUDE_PATH unset CPP_INCLUDE_PATH unset LIBRARY_PATH unset LD_LIBRARY_PATH unset DYLD_LIBRARY_PATH unset CFLAGS unset CPPFLAGS unset CXXFLAGS unset LIBS unset LDFLAGS unset CC unset CXX unset FC unset F77 unset F90 } parse_version() { local v="$1" V="$1" USE_FLAGS=${USE_FLAGS:-''} local tmp='' if [[ "$v" =~ "_" ]]; then tmp="${v#*_}" USE_FLAGS+=":${tmp//_/:}:" v="${v%%_*}" fi V_PKG="${v%%-*}" # version without the release number if [[ $v == *-* ]]; then V_RELEASE="${v#*-}" # release number fi case "${V_PKG}" in *.*.* ) V_MAJOR="${V_PKG%%.*}" tmp="${V_PKG#*.}" V_MINOR="${tmp%%.*}" V_PATCHLVL="${tmp#*.}" ;; *.* ) V_MAJOR="${V_PKG%.*}" V_MINOR="${V_PKG#*.}" ;; * ) V_MAJOR="${V_PKG}" ;; esac VERSIONS=() if [[ -n ${V_RELEASE} ]]; then VERSIONS+=( "${V_PKG}-${V_RELEASE}" ) fi if [[ -n ${V_PATCHLVL} ]]; then VERSIONS+=( "${V_MAJOR}.${V_MINOR}.${V_PATCHLVL}" ) fi if [[ -n ${V_MINOR} ]]; then VERSIONS+=( "${V_MAJOR}.${V_MINOR}" ) fi VERSIONS+=( "${V_MAJOR}" ) } # these variables must be export for envsubst(1) declare -x P='' declare -x V='' declare -x V_PKG='' declare -x V_MAJOR='' # first number in version string declare -x V_MINOR='' # second number in version string (or empty) declare -x V_PATCHLVL='' # third number in version string (or empty) declare -x V_RELEASE='' # module release (or empty) declare -x PREFIX='' declare -A SHASUMS=() declare -a MODULE_DOCFILES=() declare -A Yaml_valid_keys_for_module=( ['defaults']=1 ['shasums']=1 ['type']=1 ['versions']=1 ) declare -A Yaml_default_config=( ['build_requires']='' # !!seq of strings ['compile_in_sourcetree']='no' # !!str ['configure_with']='auto' # !!str ['configure_args']='' # !!seq of strings ['configure_args+']='' # !!seq of strings ['default_variant']='' # !!str ['docfiles']='' # !!seq of strings ['docfiles+']='' # !!seq of strings ['download_dir']='' # !!str ['group']='Tools' # !!str ['kernels']='' # !!seq of strings ['group_deps']='' # !!map ['overlay']='base' # !!str ['patch_files']='' # !!seq ['patch_files+']='' # !!seq ['relstage']='unstable' # !!str ['runtime_deps']='' # !!seq of strings ['script']='build' # !!str ['suffix']='' # !!str ['systems']='' # !!seq of strings ['sub_packages']='' # !!map ['target_cpus']='' # !!seq of strings ['urls']='' # !!map ['variant']='' # !!str ) declare -A Yaml_valid_vk_keys=( ['config']='1' # !!map ['variants']='1' # !!map ) declare -A Unpackers=( ['tar']='tar' ['7z']='7z' ) declare -A KernelNames=( ['linux']='1' ['darwin']='1' ['any']='1' ) declare -A TargetCPUs=( ['x86_64']='x86_64' ['arm64']='arm64' ['aarch64']='arm64' ['any']='1' ) declare -A hierarchical_groups=( ['compiler']='compiler' ['mpi']='compiler mpi' ['hdf5']='compiler mpi hdf5' ['hdf5_serial']='compiler hdf5_serial' ) build_modules_yaml_v1(){ : " " local -- yaml_mod_config="$1" local -- name="$2" local -- version="$3" shift 3 local -a with_modules=( "$@" ) check_yaml_keys(){ local -n valid_yaml_keys="$1" local -n used_yaml_keys="$2" used_yaml_keys=() local -- key='' local -a keys=() readarray -t keys < <( ${yq} '.|keys().[]' 2>/dev/null) debug "top-level keys: ${keys[*]}" for key in "${keys[@]}"; do [[ -v valid_yaml_keys[${key}] ]] || \ std::die 3 "Invalid key in YAML configuration file -- ${key}" used_yaml_keys[${key}]=1 done } get_config(){ local -- yaml_input="$1" local -n cfg="$2" # ref. to return configuration local -n dfl="$3" # ref. to defaults local -- key='' local -- value='' for key in "${!dfl[@]}"; do cfg[${key}]="${dfl[${key}]}" done if [[ -z "${yaml_input}" ]]; then return 0 fi local -a keys=() readarray -t keys < <( ${yq} -e ".|keys().[]" <<<"${yaml_input}" 2>/dev/null ) || \ std::die 3 "Oops: retrieving keys from:\n${yaml_input}" debug "config keys: ${keys[*]}" for key in "${keys[@]}"; do [[ -v dfl[${key,,}] ]] || \ std::die 3 "%s -- %s\n%s" \ "Invalid key in configuration" \ "${key}" "${yaml_input}" case ${key} in compile_in_sourcetree ) pm::get_value "${yaml_input}" value "${key}" '!!bool' case ${value,,} in true ) cfg[${key,,}]='yes' ;; false ) cfg[${key,,}]='no' ;; * ) std::die 3 "%s '%s' -- %s" \ "Invalid value for" \ "${key}" \ "${value}" ;; esac ;; configure_with ) pm::get_value "${yaml_input}" value "${key}" '!!str' case ${value,,} in auto | cmake | autotools ) cfg[${key,,}]="${value,,}" ;; * ) std::die 3 "%s '%s' -- %s" \ "Invalid value for" \ 'configure_with' \ "${value}" ;; esac ;; default_variant | download_dir | group | overlay | script | suffix ) pm::get_value "${yaml_input}" value "${key}" '!!str' cfg[${key,,}]="${value}" ;; group_deps ) pm::get_value "${yaml_input}" value "${key}" '!!map' cfg[${key,,}]="${value}" ;; relstage ) pm::get_value "${yaml_input}" value "${key}" '!!str' case ${value,,} in unstable | stable | deprecated ) cfg[${key,,}]="${value,,}" ;; remove | removed ) cfg[${key,,}]='remove' ;; * ) std::die 3 "%s '%s' -- %s" \ "Invalid value for" \ 'relstage' \ "${value}" ;; esac ;; urls | sub_packages ) pm::get_value "${yaml_input}" value "${key}" '!!seq' cfg[${key,,}]="${value}" ;; build_requires|configure_args|docfiles|patch_files|runtime_deps|systems|variant ) pm::get_seq "${yaml_input}" value "${key}" cfg[${key,,}]="${value}" ;; kernels ) pm::get_seq "${yaml_input}" value "${key}" set -o noglob local -a items=( "${value,,}" ) set +o noglob local -- item='' for item in "${items[@]}"; do [[ -v KernelNames[${item}] ]] || \ die_invalid_value \ "${yaml_input}" \ 'config section' \ 'kernel' \ "${item}" done cfg[${key,,}]="${value}" ;; target_cpus ) pm::get_seq "${yaml_input}" value "${key}" set -o noglob local -a items=( "${value,,}" ) set +o noglob local -- item='' for item in "${items[@]}"; do [[ -v TargetCPUs[${item}] ]] || \ die_invalid_value \ "${yaml_input}" \ 'config section' 'CPU' \ "${item}" done cfg[${key,,}]="${value}" ;; 'configure_args+' | 'docfiles+' | 'patch_files+' ) pm::get_seq "${yaml_input}" value "${key}" key="${key:0:-1}" if [[ -z "${cfg[${key,,}]}" ]]; then cfg[${key,,}]="${value}" else cfg[${key,,}]+=$'\n'"${value}" fi ;; * ) std::die 3 "%s '%s' in %s" \ "Oops unhandled key" \ "${key}" \ "${FUNCNAME[0]}" esac done } get_matching_version_keys(){ : " return list of version keys in refvar $1 matching version specified in $2. " local -n refvar="$1" local version="$2" local -a keys=() readarray -t keys < <( ${yq} -e '.versions|keys().[]' 2>/dev/null ) || \ std::die 3 "No version keys in configuration file!" refvar=() for key in "${keys[@]}"; do l=() # loop over semicolon separated list of keys for k in ${key//;/ }; do # brace expansion of key local list=() list=( $(${bash} -c "echo $k") ) if [[ ${list[@]} =~ ${version} ]]; then refvar+=("${key}") break fi done done (( ${#refvar[@]} == 0 )) && \ std::die 3 "No configuration for version -- ${version}" return 0 } get_yaml_vk_config(){ : " Get configuration for version key $1. Please note: this can be the empty string. " local -- key="$1" result=$( ${yq} ".versions.\"${key}\"" 2>/dev/null ) || \ result="" echo "${result}" } get_variants(){ local yaml yaml=$(${yq} '.variants' 2>/dev/null) echo "${yaml}" } get_num_variants(){ local -i n=0 n=$(${yq} '.|length' 2>/dev/null) echo "$n" } get_variant(){ local n="$1" local yaml yaml=$(${yq} -e ".[$n]" 2>/dev/null) [[ "${yaml}" == 'null' ]] && _result='' echo "${yaml}" } chk_group_deps(){ : " Check the group dependencies: 1. are all keys valid? 2. all required group deps defined? 3. more group deps defined as required? " : local -- yaml="$1" # yaml formatted string: value of group_deps local -- group="$2" # compiler|mpi|hdf5|hdf5_serial local -- name="$3" # module name local -- version="$4" # module version # query all specified group dependencies local -a keys=() readarray -t keys < <( ${yq} ".|keys|.[]" <<<"${yaml}" 2>/dev/null ) local -- key='' for key in "${keys[@]}"; do # is this a name of a hierarchical group? [[ -v hierarchical_groups[${key}] ]] || \ die_illegal_group_dep "${name}" "${version}" "${group}" "${key}" # is this in the list of required group dependencies? is_in_array "${key}" "${hierarchical_groups[${key}]}" || \ die_invalid_group_dep "${name}" "${version}" "${group}" done # are all required group dependencies defined? for key in ${hierarchical_groups[${group,,}]}; do is_in_array "${key,,}" "${keys[@]}" || \ die_missing_group_dep "${name}" "${version}" "${group}" done } get_group_deps(){ local -- yaml="$1" # yaml formatted string with group config local -- group="$2" # compiler|mpi|hdf5|hdf5_serial local -n with_modules="$3" # refvar to return a list of modules local -a modules=() local keys=() readarray -t keys < <( ${yq} ".${group,,}|keys|.[]" <<<"${yaml}" 2>/dev/null ) local key for key in "${keys[@]}"; do local versions=() readarray -t versions < <( ${yq} -e ".${group,,}.${key}[]" <<<"${yaml}" 2>/dev/null ) local version for version in "${versions[@]}"; do if [[ -v opt_with_dict[${key}/${version}] ]]; then with_modules+=( "${key}/${version}" ) fi modules+=( "${key}/${version}" ) done done if (( ${#with_modules[@]} == 0 )); then with_modules=( "${modules[@]}" ) fi } die_missing_group_dep(){ std::die 3 "%s/%s: %s" \ "${1}" "${2}" \ "is in group '$3', but the group dependency for this group is missing!" } die_invalid_group_dep(){ std::die 3 "%s/%s: %s" \ "${1}" "${2}" \ "invalid group dependency '$3' for module in group '$4'!" } die_illegal_group_dep(){ std::die 3 "%s/%s: %s" \ "${1}" "${2}" \ "illegal group dependency '$4' for module in group '$3'!" } is_in_array(){ local -r key="$1" shift 1 [[ $* =~ (^|[[:space:]])"${key}"($|[[:space:]]) ]] } is_subset(){ local -n subset="$1" shift 1 local el='' for el in "${subset[@]}"; do is_in_array "${el}" "$@" || return 1 done return 0 } : " To compile a module with a certain compiler||mpi||hdf5 dependency the '--with' option can be used. Depending on the hierarchical group the modules specified with the option '--with' must be a subset of - compiler: ( compiler) - mpi: ( compiler mpi ) - hdf5: ( compiler mpi hdf5 ) - hdf5_serial: ( compiler hdf5_serial ) " build_modules_compiler(){ local -- module_name="$1" local -- module_version="$2" local -n module_cfg="$3" chk_group_deps "${module_cfg['group_deps']}" 'Compiler' \ "${module_name}" "${module_version}" local -a with_compiler=() get_group_deps "${module_cfg['group_deps']}" 'Compiler' with_compiler debug "${with_compiler[@]}" local compiler='' for compiler in "${with_compiler[@]}"; do # build if opt_with_modules is empty or compiler is in this array (( ${#opt_with_modules[@]} != 0 )) \ && [[ "${compiler}" != "${opt_with_modules[0]}" ]] \ && continue [[ "${opt_check_mode}" == 'yes' ]] && continue pbuild.build_module_yaml \ "${module_name}" "${module_version}" \ "$3" \ "${compiler}" \ "${@:4}" done } build_modules_hdf5_serial(){ local -- module_name="$1" local -- module_version="$2" local -n module_cfg="$3" chk_group_deps "${module_cfg['group_deps']}" 'HDF5_serial' \ "${module_name}" "${module_version}" local -a with_compiler=() get_group_deps "${module_cfg['group_deps']}" 'Compiler' with_compiler local -a with_hdf5=() get_group_deps "${module_cfg['group_deps']}" 'HDF5_serial' with_hdf5 local -- compiler local -- hdf5 for compiler in "${with_compiler[@]}"; do for hdf5 in "${with_hdf5[@]}"; do # build if opt_with_modules is empty or compiler is in this array (( ${#opt_with_modules[@]} != 0 )) \ && ! is_subset opt_with_modules "${compiler}" "${hdf5}" \ && continue debug "build $module_name/$module_version with $compiler and $hdf5" debug " runtime deps: ${runtime_deps[*]}" debug " build requires: ${build_requires[*]}" pbuild.build_module_yaml \ "${module_name}" "${module_version}" \ "$3" \ "${compiler}" \ "${hdf5}" \ "${@:4}" done done } build_modules_mpi(){ local -- module_name="$1" local -- module_version="$2" local -n module_cfg="$3" chk_group_deps "${module_cfg['group_deps']}" 'MPI' "${module_name}" "${module_version}" local -a with_compiler=() get_group_deps "${module_cfg['group_deps']}" 'Compiler' with_compiler local -a with_mpi=() get_group_deps "${module_cfg['group_deps']}" 'MPI' with_mpi local -- compiler local -- mpi for compiler in "${with_compiler[@]}"; do for mpi in "${with_mpi[@]}"; do # build if opt_with_modules is empty or compiler is in this array (( ${#opt_with_modules[@]} != 0 )) \ && ! is_subset opt_with_modules "${compiler}" "${mpi}" \ && continue debug "build $module_name/$module_version with $compiler and $mpi" debug " runtime deps: ${runtime_deps[*]}" debug " build requires: ${build_requires[*]}" pbuild.build_module_yaml \ "${module_name}" "${module_version}" \ "$3" \ "${compiler}" \ "${mpi}" \ "${@:4}" done done } build_modules_hdf5(){ local -- module_name="$1" local -- module_version="$2" local -n module_cfg="$3" chk_group_deps "${module_cfg['group_deps']}" 'HDF5' "${module_name}" "${module_version}" local -a with_compiler=() get_group_deps "${module_cfg['group_deps']}" 'Compiler' with_compiler local -a with_mpi=() get_group_deps "${module_cfg['group_deps']}" 'MPI' with_mpi local -a with_hdf5=() get_group_deps "${module_cfg['group_deps']}" 'HDF5' with_hdf5 local -- compiler local -- mpi local -- hdf5 for compiler in "${with_compiler[@]}"; do for mpi in "${with_mpi[@]}"; do for hdf5 in "${with_hdf5[@]}"; do debug "build $module_name/$module_version with $compiler, $mpi and $hdf5" debug " runtime deps: ${runtime_deps[*]}" debug " build requires: ${build_requires[*]}" # build if opt_with_modules is empty or compiler is in this array (( ${#opt_with_modules[@]} != 0 )) \ && ! is_subset opt_with_modules \ "${compiler}" "${mpi}" "${hdf5}" \ && continue pbuild.build_module_yaml \ "${module_name}" "${module_version}" \ "$3" \ "${compiler}" \ "${mpi}" \ "${hdf5}" \ "${@:4}" done done done } build_modules_other(){ local -- module_name="$1" local -- module_version="$2" local -n module_cfg="$3" pbuild.build_module_yaml \ "${module_name}" "${module_version}" \ "$3" \ "${@:4}" } die_parsing(){ std::die 3 "error parsing YAML:\n----\n$1\n----" } die_invalid_value(){ std::die 3 "Invalid value for key '$3' in $2 -- '$4'\n----\n$1\n----" } die_invalid_key(){ std::die 3 "Invalid key '$3' in $2\n----\n$1\n----" } die_missing_key(){ std::die 3 "Key '$3' missing in $2\n----\n$1\n----" } set_urls() { local -- yaml="$1" local -i l=0 l=$( ${yq} -Ne e '.|length' <<<"${yaml}" 2>/dev/null) || \ die_parsing "{yaml}" local -i i=0 local -- url_yaml='' for ((i=0; i/dev/null) || \ die_parsing "{yaml}" local url='' local fname='' local strip_dirs='' local unpacker='' local key='' local value='' while read -r key value; do key=${key:0:-1} case "${key}" in url ) url=$(${envsubst} <<<"${value}") ;; name ) fname=$(${envsubst} <<<"${value}") ;; strip_dirs ) [[ ${value} =~ ^[0-9]+$ ]] || \ die_invalid_value \ "${url_yaml}" \ 'list of URLS' \ 'strip_dirs' \ "${value}" strip_dirs="${value}" ;; unpacker ) [[ -v Unpackers[${value}] ]] || \ die_invalid_value \ "${url_yaml}" \ 'list of URLs' \ 'unpacker' \ "${value}" unpacker="${value}" ;; * ) die_invalid_key \ "${url_yaml}" \ 'list of URLs' \ "${key}" ;; esac done <<<"${url_yaml}" [[ -z "${url}" ]] && \ die_missing_key \ "${url_yaml}" \ 'list of URLs' \ 'url' [[ -z "${fname}" ]] && fname="${url##*/}" [[ -z "${strip_dirs}" ]] && strip_dirs=1 [[ -z "${unpacker}" ]] && unpacker='tar' pbuild.set_urls "${url}" "${fname}" "${strip_dirs}" "${unpacker}" done } set_configure_args(){ local -a args=() readarray -t args <<< "$1" pbuild.add_configure_args "${args[@]}" } set_patch_files(){ local -a args=() readarray -t args <<< "$1" pbuild.add_patch_files "${args[@]}" } die_sub_package_name_missing(){ std::die 3 "Name of sub-package not specified in \n===\n$1\n===\n" } die_sub_package_version_missing(){ std::die 3 "Version of sub-package not specified in \n===\n$1\n===\n" } build_sub_packages(){ local -- yaml="$1" local -i l=0 l=$( ${yq} -Ne e '.|length' <<<"${yaml}" 2>/dev/null) || \ die_parsing "${yaml}" local -i i=0 local -- fname='' local -- pkgs_yaml='' for ((i=0; i/dev/null) || \ die_parsing "${yaml}" local -- pkg_name='' local -- pkg_version='' local -a pkg_build_args=() local -a keys=() readarray -t keys < <( ${yq} -e ".|keys().[]" <<<"${pkgs_yaml}" 2>/dev/null ) || \ die_parsing "${pkgs_yaml}" local -- key='' for key in "${keys[@]}"; do case ${key,,} in 'name' ) pm::get_value "${pkgs_yaml}" pkg_name "${key}" '!!str' ;; 'version' ) pm::get_value "${pkgs_yaml}" pkg_version "${key}" '!!str' ;; 'build_args' ) local -- value='' pm::get_seq "${pkgs_yaml}" value "${key}" readarray -t pkg_build_args <<< "${value}" ;; * ) die_invalid_key \ "${pkgs_yaml}" \ "in subpackage '$i'" \ "${key}" ;; esac done [[ -n "${pkg_name}" ]] || \ die_sub_package_name_missing "${pkgs_yaml}" [[ -n "${pkg_version}" ]] || \ die_sub_package_version_missing "${pkgs_yaml}" [[ "${opt_verbose}" == 'yes' ]] && \ pkg_build_args+=( '--verbose' ) [[ "${opt_debug}" == 'yes' ]] && \ pkg_build_args+=( '--debug' ) [[ "${opt_force_rebuild}" == 'yes' ]] && \ pkg_build_args+=( '-f' ) pkg_build_args+=( "--parent-prefix=${PREFIX}" ) "$BUILDBLOCK_DIR/build-${pkg_name}" \ "${pkg_name}/${pkg_version}" \ "${pkg_build_args[@]}" done } build_modules_variant(){ local -- module_name="$1" local -- module_version="$2" local -n module_config="$3" check_system(){ [[ -z ${module_config['systems']} ]] && return 0 set -o noglob local -a systems=( ${module_config['systems']} ) set +o noglob local -- system for system in "${systems[@]}"; do [[ "${opt_system}" == ${system} ]] && return 0 [[ "${HOSTNAME}" == ${system} ]] && return 0 done std::info "Skipping variant '${module_version}', neither OS nor hostname match:" std::info " This system: ${opt_system}; hostname: ${HOSTNAME}" std::info " Systems to build on: ${systems[@]}" return 1 } die_invalid_kernel_name(){ std::die 3 "Invalid kernel name in configuration!" } check_kernel(){ [[ -z ${module_config['kernels']} ]] && return 0 set -o noglob local -a kernels=( "${module_config['kernels'],,}" ) set +o noglob local -- kernel='' for kernel in "${kernels[@]}"; do [[ ${kernel} == 'any' ]] && return 0 [[ ${kernel} == ${KernelName,,} ]] & return 0 done std::info "Skipping variant '${module_version}':" std::info " The kernel of this systems is: ${KernelName}" std::info " But the variant is for the following kernels: ${module_config['kernels']}" return 1 } check_target_cpu(){ [[ -z ${module_config['target_cpus']} ]] && return 0 set -o noglob local -a target_cpus=( "${module_config['target_cpus'],,}" ) set +o noglob local -- system_cpu=$(uname -p) local -- cpu='' for cpu in "${target_cpus[@]}"; do [[ ${cpu} == 'any' ]] && return 0 [[ ${cpu} == ${system_cpu} ]] && return 0 done std::info "Skipping variant '${module_version}':" std::info " The CPU of this systems is: ${system_cpu}" std::info " But this variant is for the following CPUs: ${module_config['target_cpus']}" return 1 } P="${module_name}" parse_version "${module_version}" local build_variant="${opt_variant:-${module_config['default_variant']}}" # build this variant? if [[ ":${module_config['variant']}:" != *:${build_variant}:* ]]; then debug "don't build this variant: ${module_config['variant']} != *:${build_variant}:*" return 0 fi # build for this system, kernel and target_cpu? check_system || return 0 check_kernel || return 0 check_target_cpu || return 0 debug "build variant ${module_name}/${module_version}" local ol_name="${module_config['overlay']}" [[ -v OverlayInfo[${ol_name}:install_root] ]] || \ std::die 2 "%s" \ "Overlay doesn't exist - ${ol_name}" declare ol_install_root="${OverlayInfo[${ol_name}:install_root]}" declare ol_modulefiles_root="${OverlayInfo[${ol_name}:modulefiles_root]}" module_version+="${module_config['suffix']}" pbuild.compile_in_sourcetree "${module_config['compile_in_sourcetree']}" pbuild.configure_with "${module_config['configure_with']}" pbuild.add_to_group "${module_config['group']}" set_urls "${module_config['urls']}" set_configure_args "${module_config['configure_args']}" set_patch_files "${module_config['patch_files']}" if [[ -n "${module_config['download_dir']}" ]]; then PMODULES_DISTFILESDIR="${module_config['download_dir']}" fi local -a runtime_deps=() if [[ -n ${module_config['runtime_deps']} ]]; then readarray -t runtime_deps <<<"${module_config['runtime_deps']}" debug "runtime_deps=${runtime_deps[@]} length: ${#runtime_deps[@]}" fi local -a build_requires=() if [[ -n ${module_config['build_requires']} ]]; then readarray -t build_requires <<<"${module_config['build_requires']}" build_requires=( "${build_requires[@]/#/b:}" ) debug "build_requires=${build_requires[@]} length: ${#build_requires[@]}" fi if [[ -n ${module_config['docfiles']} ]]; then readarray -t MODULE_DOCFILES <<<"${module_config['docfiles']}" fi local -A build_functions=( ['Compiler']=build_modules_compiler [HDF5_serial]=build_modules_hdf5_serial [MPI]=build_modules_mpi [HDF5]=build_modules_hdf5 ) local func=build_modules_other if [[ -v build_functions[${module_config['group']}] ]]; then func=${build_functions[${module_config['group']}]} fi ${func} \ "${module_name}" \ "${module_version}" \ module_config \ "${runtime_deps[@]}" "${build_requires[@]}" build_sub_packages "${module_config['sub_packages']}" } expand_version_key(){ local -n ev_result="$1" local -- key="$2" local -- version="$3" ev_result=() # loop over comma separated list of keys for k in ${key//;/ }; do # do curly brackets expansion {} local l local list=() list=( $(${bash} -c "echo $k" ) ) for l in "${list[@]}"; do if [[ $l =~ ${version} ]]; then ev_result+=("${l}") fi done done } local -A used_keys=() local -- yaml_input='' local -A default_config=() local -- version_key='' local -a version_keys=() local -A shasums=() check_yaml_keys Yaml_valid_keys_for_module used_keys <<<"${yaml_mod_config}" [[ -v used_keys['versions'] ]] || \ std::die 3 "No version(s) specified in YAML configuration file." if [[ -v used_keys['defaults'] ]]; then yaml_input=$(${yq} '.defaults' <<<"${yaml_mod_config}" 2>/dev/null) fi get_config "${yaml_input}" default_config Yaml_default_config if [[ -v used_keys['shasums'] ]]; then local yaml_input='' yaml_input=$(${yq} '.shasums' <<<"${yaml_mod_config}" 2>/dev/null) while read -r key value; do [[ -z ${key} ]] && continue SHASUMS[${key//:}]="${value}" done <<<"${yaml_input}" fi if [[ -v used_keys['type'] ]]; then local -- value='' pm::get_value "${yaml_mod_config}" value 'type' '!!str' case "${value,,}" in 'module' ) [[ "${module_type}" == 'sub_package' ]] && \ std::die 3 "Module type is 'module' but was called as 'sub_package'!" ;; 'sub_package' ) [[ "${module_type}" == 'module' ]] && \ std::die 3 "Module type is 'sub_package' but was called as 'module'!" ;; * ) std::die 3 "Invalid module type -- '${value}'!" ;; esac fi get_matching_version_keys version_keys "${version}" <<<"${yaml_mod_config}" for version_key in "${version_keys[@]}"; do local yaml_vk_config='' yaml_vk_config=$(get_yaml_vk_config "${version_key}" <<<"${yaml_mod_config}") # check keys: allowed are 'config' and 'variants' used_keys=() check_yaml_keys Yaml_valid_vk_keys used_keys <<<"${yaml_vk_config}" # read config if set local -A vk_config=() yaml_input='' if [[ -v used_keys['config'] ]]; then yaml_input=$(${yq} '.config' <<<"${yaml_vk_config}" 2>/dev/null) debug "vk input: ${yaml_input}" fi # reminder: if YAML input is empty, next line copies defaults to 'vk_config' get_config "${yaml_input}" vk_config default_config local -- yaml_variants='' yaml_variants=$(get_variants <<<"${yaml_vk_config}") local -i num_variants=0 num_variants=$(get_num_variants <<<"${yaml_variants}") local versions=() expand_version_key versions "${version_key}" "${version}" local v='' for v in "${versions[@]}"; do debug "version: $v" if (( num_variants == 0 )); then build_modules_variant \ "${name}" "${v}" \ vk_config else local -i n=0 local -- yaml_variant='' for ((n=0; n&2 } #............................................................................. # main init_module_environment parse_args "$@" pbuild.jobs "${opt_jobs}" pbuild.force_rebuild "${opt_force_rebuild}" pbuild.build_target "${opt_build_target}" pbuild.dry_run "${opt_dry_run}" pbuild.enable_cleanup_build "${opt_enable_cleanup_build}" pbuild.enable_cleanup_src "${opt_enable_cleanup_src}" pbuild.update_modulefiles "${opt_update_modulefiles}" pbuild.system "${opt_system}" pbuild.verbose "${opt_verbose}" # # read configuration for modbuild # pm::read_config # :FIXME: should dist files go to # ${pm_root}/var/distfiles # or # ${overlay}/var/distfiles # ? PMODULES_DISTFILESDIR=${PMODULES_DISTFILESDIR:-"${PMODULES_HOME%%/Tools*}/var/distfiles"} PMODULES_TMPDIR="${PMODULES_TMPDIR:-/var/tmp/${USER}}" export PMODULES_DISTFILESDIR PMODULES_TMPDIR declare -r BUILD_SCRIPT declare -r BUILDBLOCK_DIR if [[ -z ${module_name} ]]; then # the module name is defined by the directory the build script is in IFS=/ read -r -a fname <<< "${BUILD_SCRIPT:1}" module_name=${fname[${#fname[@]}-2]} fi for version in ${versions_to_build}; do build_modules "${module_name}" "${version}" "${opt_with_modules[@]}" done # Local Variables: # mode: sh # sh-basic-offset: 8 # tab-width: 8 # End: