| # Copyright 1999-2018 Gentoo Foundation |
| # Distributed under the terms of the GNU General Public License v2 |
| |
| # @ECLASS: multibuild.eclass |
| # @MAINTAINER: |
| # Michał Górny <mgorny@gentoo.org> |
| # @AUTHOR: |
| # Author: Michał Górny <mgorny@gentoo.org> |
| # @SUPPORTED_EAPIS: 4 5 6 7 |
| # @BLURB: A generic eclass for building multiple variants of packages. |
| # @DESCRIPTION: |
| # The multibuild eclass aims to provide a generic framework for building |
| # multiple 'variants' of a package (e.g. multilib, Python |
| # implementations). |
| |
| case "${EAPI:-0}" in |
| 0|1|2|3) |
| die "Unsupported EAPI=${EAPI:-0} (too old) for ${ECLASS}" |
| ;; |
| 4|5|6|7) |
| ;; |
| *) |
| die "Unsupported EAPI=${EAPI} (unknown) for ${ECLASS}" |
| ;; |
| esac |
| |
| if [[ ! ${_MULTIBUILD} ]]; then |
| |
| # @ECLASS-VARIABLE: MULTIBUILD_VARIANTS |
| # @DESCRIPTION: |
| # An array specifying all enabled variants which multibuild_foreach* |
| # can execute the process for. |
| # |
| # In ebuild, it can be set in global scope. Eclasses should set it |
| # locally in function scope to support nesting properly. |
| # |
| # Example: |
| # @CODE |
| # python_foreach_impl() { |
| # local MULTIBUILD_VARIANTS=( python{2_5,2_6,2_7} ... ) |
| # multibuild_foreach_variant python_compile |
| # } |
| # @CODE |
| |
| # @ECLASS-VARIABLE: MULTIBUILD_VARIANT |
| # @DESCRIPTION: |
| # The current variant which the function was executed for. |
| # |
| # Example value: |
| # @CODE |
| # python2_6 |
| # @CODE |
| |
| # @ECLASS-VARIABLE: MULTIBUILD_ID |
| # @DESCRIPTION: |
| # The unique identifier for a multibuild run. In a simple run, it is |
| # equal to MULTIBUILD_VARIANT. In a nested multibuild environment, it |
| # contains the complete selection tree. |
| # |
| # It can be used to create variant-unique directories and files. |
| # |
| # Example value: |
| # @CODE |
| # amd64-double |
| # @CODE |
| |
| # @ECLASS-VARIABLE: BUILD_DIR |
| # @DESCRIPTION: |
| # The current build directory. In global scope, it is supposed |
| # to contain an 'initial' build directory. If unset, ${S} is used. |
| # |
| # multibuild_foreach_variant() sets BUILD_DIR locally |
| # to variant-specific build directories based on the initial value |
| # of BUILD_DIR. |
| # |
| # Example value: |
| # @CODE |
| # ${WORKDIR}/foo-1.3-python2_6 |
| # @CODE |
| |
| # @FUNCTION: multibuild_foreach_variant |
| # @USAGE: [<argv>...] |
| # @DESCRIPTION: |
| # Run the passed command repeatedly for each of the enabled package |
| # variants. |
| # |
| # Each of the runs will have variant-specific BUILD_DIR set, and output |
| # teed to a separate log in ${T}. |
| # |
| # The function returns 0 if all commands return 0, or the first non-zero |
| # exit status otherwise. However, it performs all the invocations |
| # nevertheless. It is preferred to call 'die' inside of the passed |
| # function. |
| multibuild_foreach_variant() { |
| debug-print-function ${FUNCNAME} "${@}" |
| |
| [[ ${MULTIBUILD_VARIANTS} ]] \ |
| || die "MULTIBUILD_VARIANTS need to be set" |
| |
| local bdir=${BUILD_DIR:-${S}} |
| |
| # Avoid writing outside WORKDIR if S=${WORKDIR}. |
| [[ ${bdir%%/} == ${WORKDIR%%/} ]] && bdir=${WORKDIR}/build |
| |
| local prev_id=${MULTIBUILD_ID:+${MULTIBUILD_ID}-} |
| local ret=0 lret=0 v |
| |
| debug-print "${FUNCNAME}: initial build_dir = ${bdir}" |
| |
| for v in "${MULTIBUILD_VARIANTS[@]}"; do |
| local MULTIBUILD_VARIANT=${v} |
| local MULTIBUILD_ID=${prev_id}${v} |
| local BUILD_DIR=${bdir%%/}-${v} |
| |
| _multibuild_run() { |
| # find the first non-private command |
| local i=1 |
| while [[ ${!i} == _* ]]; do |
| (( i += 1 )) |
| done |
| |
| [[ ${i} -le ${#} ]] && einfo "${v}: running ${@:${i}}" |
| "${@}" |
| } |
| |
| _multibuild_run "${@}" \ |
| > >(exec tee -a "${T}/build-${MULTIBUILD_ID}.log") 2>&1 |
| lret=${?} |
| done |
| [[ ${ret} -eq 0 && ${lret} -ne 0 ]] && ret=${lret} |
| |
| return ${ret} |
| } |
| |
| # @FUNCTION: multibuild_parallel_foreach_variant |
| # @USAGE: [<argv>...] |
| # @DESCRIPTION: |
| # Run the passed command repeatedly for each of the enabled package |
| # variants. This used to run the commands in parallel but now it's |
| # just a deprecated alias to multibuild_foreach_variant. |
| # |
| # The function returns 0 if all commands return 0, or the first non-zero |
| # exit status otherwise. However, it performs all the invocations |
| # nevertheless. It is preferred to call 'die' inside of the passed |
| # function. |
| multibuild_parallel_foreach_variant() { |
| debug-print-function ${FUNCNAME} "${@}" |
| |
| [[ ${EAPI} == [45] ]] || die "${FUNCNAME} is banned in EAPI ${EAPI}" |
| |
| multibuild_foreach_variant "${@}" |
| } |
| |
| # @FUNCTION: multibuild_for_best_variant |
| # @USAGE: [<argv>...] |
| # @DESCRIPTION: |
| # Run the passed command once, for the best of the enabled package |
| # variants. |
| # |
| # The run will have a proper, variant-specificBUILD_DIR set, and output |
| # teed to a separate log in ${T}. |
| # |
| # The function returns command exit status. |
| multibuild_for_best_variant() { |
| debug-print-function ${FUNCNAME} "${@}" |
| |
| [[ ${MULTIBUILD_VARIANTS} ]] \ |
| || die "MULTIBUILD_VARIANTS need to be set" |
| |
| # bash-4.1 can't handle negative subscripts |
| local MULTIBUILD_VARIANTS=( |
| "${MULTIBUILD_VARIANTS[$(( ${#MULTIBUILD_VARIANTS[@]} - 1 ))]}" |
| ) |
| multibuild_foreach_variant "${@}" |
| } |
| |
| # @FUNCTION: multibuild_copy_sources |
| # @DESCRIPTION: |
| # Create per-variant copies of source tree. The source tree is assumed |
| # to be in ${BUILD_DIR}, or ${S} if the former is unset. The copies will |
| # be placed in directories matching BUILD_DIRs used by |
| # multibuild_foreach(). |
| multibuild_copy_sources() { |
| debug-print-function ${FUNCNAME} "${@}" |
| |
| local _MULTIBUILD_INITIAL_BUILD_DIR=${BUILD_DIR:-${S}} |
| |
| einfo "Will copy sources from ${_MULTIBUILD_INITIAL_BUILD_DIR}" |
| |
| local cp_args=() |
| if cp --reflink=auto --version &>/dev/null; then |
| # enable reflinking if possible to make this faster |
| cp_args+=( --reflink=auto ) |
| fi |
| |
| _multibuild_create_source_copy() { |
| einfo "${MULTIBUILD_VARIANT}: copying to ${BUILD_DIR}" |
| cp -p -R "${cp_args[@]}" \ |
| "${_MULTIBUILD_INITIAL_BUILD_DIR}" "${BUILD_DIR}" || die |
| } |
| |
| multibuild_foreach_variant _multibuild_create_source_copy |
| } |
| |
| # @FUNCTION: run_in_build_dir |
| # @USAGE: <argv>... |
| # @DESCRIPTION: |
| # Run the given command in the directory pointed by BUILD_DIR. |
| run_in_build_dir() { |
| debug-print-function ${FUNCNAME} "${@}" |
| local ret |
| |
| [[ ${#} -ne 0 ]] || die "${FUNCNAME}: no command specified." |
| [[ ${BUILD_DIR} ]] || die "${FUNCNAME}: BUILD_DIR not set." |
| |
| mkdir -p "${BUILD_DIR}" || die |
| pushd "${BUILD_DIR}" >/dev/null || die |
| "${@}" |
| ret=${?} |
| popd >/dev/null || die |
| |
| return ${ret} |
| } |
| |
| # @FUNCTION: multibuild_merge_root |
| # @USAGE: <src-root> <dest-root> |
| # @DESCRIPTION: |
| # Merge the directory tree (fake root) from <src-root> to <dest-root> |
| # (the real root). Both directories have to be real, absolute paths |
| # (i.e. including ${D}). Source root will be removed. |
| multibuild_merge_root() { |
| local src=${1} |
| local dest=${2} |
| |
| local ret |
| |
| if use userland_BSD; then |
| # Most of BSD variants fail to copy broken symlinks, #447370 |
| # also, they do not support --version |
| |
| tar -C "${src}" -f - -c . \ |
| | tar -x -f - -C "${dest}" |
| [[ ${PIPESTATUS[*]} == '0 0' ]] |
| ret=${?} |
| else |
| local cp_args=() |
| |
| if cp -a --version &>/dev/null; then |
| cp_args+=( -a ) |
| else |
| cp_args+=( -P -R -p ) |
| fi |
| |
| if cp --reflink=auto --version &>/dev/null; then |
| # enable reflinking if possible to make this faster |
| cp_args+=( --reflink=auto ) |
| fi |
| |
| cp "${cp_args[@]}" "${src}"/. "${dest}"/ |
| ret=${?} |
| fi |
| |
| if [[ ${ret} -ne 0 ]]; then |
| die "${MULTIBUILD_VARIANT:-(unknown)}: merging image failed." |
| fi |
| |
| rm -rf "${src}" |
| } |
| |
| _MULTIBUILD=1 |
| fi |