Merge remote-tracking branch 'remotes/arcriley/master'
[gentoo.git] / eclass / multibuild.eclass
1 # Copyright 1999-2014 Gentoo Foundation
2 # Distributed under the terms of the GNU General Public License v2
3 # $Id$
4
5 # @ECLASS: multibuild.eclass
6 # @MAINTAINER:
7 # Michał Górny <mgorny@gentoo.org>
8 # @AUTHOR:
9 # Author: Michał Górny <mgorny@gentoo.org>
10 # @BLURB: A generic eclass for building multiple variants of packages.
11 # @DESCRIPTION:
12 # The multibuild eclass aims to provide a generic framework for building
13 # multiple 'variants' of a package (e.g. multilib, Python
14 # implementations).
15
16 case "${EAPI:-0}" in
17         0|1|2|3)
18                 die "Unsupported EAPI=${EAPI:-0} (too old) for ${ECLASS}"
19                 ;;
20         4|5|6)
21                 ;;
22         *)
23                 die "Unsupported EAPI=${EAPI} (unknown) for ${ECLASS}"
24                 ;;
25 esac
26
27 if [[ ! ${_MULTIBUILD} ]]; then
28
29 # @ECLASS-VARIABLE: MULTIBUILD_VARIANTS
30 # @DESCRIPTION:
31 # An array specifying all enabled variants which multibuild_foreach*
32 # can execute the process for.
33 #
34 # In ebuild, it can be set in global scope. Eclasses should set it
35 # locally in function scope to support nesting properly.
36 #
37 # Example:
38 # @CODE
39 # python_foreach_impl() {
40 #       local MULTIBUILD_VARIANTS=( python{2_5,2_6,2_7} ... )
41 #       multibuild_foreach_variant python_compile
42 # }
43 # @CODE
44
45 # @ECLASS-VARIABLE: MULTIBUILD_VARIANT
46 # @DESCRIPTION:
47 # The current variant which the function was executed for.
48 #
49 # Example value:
50 # @CODE
51 # python2_6
52 # @CODE
53
54 # @ECLASS-VARIABLE: MULTIBUILD_ID
55 # @DESCRIPTION:
56 # The unique identifier for a multibuild run. In a simple run, it is
57 # equal to MULTIBUILD_VARIANT. In a nested multibuild environment, it
58 # contains the complete selection tree.
59 #
60 # It can be used to create variant-unique directories and files.
61 #
62 # Example value:
63 # @CODE
64 # amd64-double
65 # @CODE
66
67 # @ECLASS-VARIABLE: BUILD_DIR
68 # @DESCRIPTION:
69 # The current build directory. In global scope, it is supposed
70 # to contain an 'initial' build directory. If unset, ${S} is used.
71 #
72 # multibuild_foreach_variant() sets BUILD_DIR locally
73 # to variant-specific build directories based on the initial value
74 # of BUILD_DIR.
75 #
76 # Example value:
77 # @CODE
78 # ${WORKDIR}/foo-1.3-python2_6
79 # @CODE
80
81 # @FUNCTION: multibuild_foreach_variant
82 # @USAGE: [<argv>...]
83 # @DESCRIPTION:
84 # Run the passed command repeatedly for each of the enabled package
85 # variants.
86 #
87 # Each of the runs will have variant-specific BUILD_DIR set, and output
88 # teed to a separate log in ${T}.
89 #
90 # The function returns 0 if all commands return 0, or the first non-zero
91 # exit status otherwise. However, it performs all the invocations
92 # nevertheless. It is preferred to call 'die' inside of the passed
93 # function.
94 multibuild_foreach_variant() {
95         debug-print-function ${FUNCNAME} "${@}"
96
97         [[ ${MULTIBUILD_VARIANTS} ]] \
98                 || die "MULTIBUILD_VARIANTS need to be set"
99
100         local bdir=${BUILD_DIR:-${S}}
101
102         # Avoid writing outside WORKDIR if S=${WORKDIR}.
103         [[ ${bdir%%/} == ${WORKDIR%%/} ]] && bdir=${WORKDIR}/build
104
105         local prev_id=${MULTIBUILD_ID:+${MULTIBUILD_ID}-}
106         local ret=0 lret=0 v
107
108         debug-print "${FUNCNAME}: initial build_dir = ${bdir}"
109
110         for v in "${MULTIBUILD_VARIANTS[@]}"; do
111                 local MULTIBUILD_VARIANT=${v}
112                 local MULTIBUILD_ID=${prev_id}${v}
113                 local BUILD_DIR=${bdir%%/}-${v}
114
115                 _multibuild_run() {
116                         # find the first non-private command
117                         local i=1
118                         while [[ ${!i} == _* ]]; do
119                                 (( i += 1 ))
120                         done
121
122                         [[ ${i} -le ${#} ]] && einfo "${v}: running ${@:${i}}"
123                         "${@}"
124                 }
125
126                 _multibuild_run "${@}" \
127                         > >(exec tee -a "${T}/build-${MULTIBUILD_ID}.log") 2>&1
128                 lret=${?}
129         done
130         [[ ${ret} -eq 0 && ${lret} -ne 0 ]] && ret=${lret}
131
132         return ${ret}
133 }
134
135 # @FUNCTION: multibuild_parallel_foreach_variant
136 # @USAGE: [<argv>...]
137 # @DESCRIPTION:
138 # Run the passed command repeatedly for each of the enabled package
139 # variants. This used to run the commands in parallel but now it's
140 # just a deprecated alias to multibuild_foreach_variant.
141 #
142 # The function returns 0 if all commands return 0, or the first non-zero
143 # exit status otherwise. However, it performs all the invocations
144 # nevertheless. It is preferred to call 'die' inside of the passed
145 # function.
146 multibuild_parallel_foreach_variant() {
147         debug-print-function ${FUNCNAME} "${@}"
148
149         [[ ${EAPI} == [45] ]] || die "${FUNCNAME} is banned in EAPI ${EAPI}"
150
151         multibuild_foreach_variant "${@}"
152 }
153
154 # @FUNCTION: multibuild_for_best_variant
155 # @USAGE: [<argv>...]
156 # @DESCRIPTION:
157 # Run the passed command once, for the best of the enabled package
158 # variants.
159 #
160 # The run will have a proper, variant-specificBUILD_DIR set, and output
161 # teed to a separate log in ${T}.
162 #
163 # The function returns command exit status.
164 multibuild_for_best_variant() {
165         debug-print-function ${FUNCNAME} "${@}"
166
167         [[ ${MULTIBUILD_VARIANTS} ]] \
168                 || die "MULTIBUILD_VARIANTS need to be set"
169
170         # bash-4.1 can't handle negative subscripts
171         local MULTIBUILD_VARIANTS=(
172                 "${MULTIBUILD_VARIANTS[$(( ${#MULTIBUILD_VARIANTS[@]} - 1 ))]}"
173         )
174         multibuild_foreach_variant "${@}"
175 }
176
177 # @FUNCTION: multibuild_copy_sources
178 # @DESCRIPTION:
179 # Create per-variant copies of source tree. The source tree is assumed
180 # to be in ${BUILD_DIR}, or ${S} if the former is unset. The copies will
181 # be placed in directories matching BUILD_DIRs used by
182 # multibuild_foreach().
183 multibuild_copy_sources() {
184         debug-print-function ${FUNCNAME} "${@}"
185
186         local _MULTIBUILD_INITIAL_BUILD_DIR=${BUILD_DIR:-${S}}
187
188         einfo "Will copy sources from ${_MULTIBUILD_INITIAL_BUILD_DIR}"
189
190         local cp_args=()
191         if cp --reflink=auto --version &>/dev/null; then
192                 # enable reflinking if possible to make this faster
193                 cp_args+=( --reflink=auto )
194         fi
195
196         _multibuild_create_source_copy() {
197                 einfo "${MULTIBUILD_VARIANT}: copying to ${BUILD_DIR}"
198                 cp -pr "${cp_args[@]}" \
199                         "${_MULTIBUILD_INITIAL_BUILD_DIR}" "${BUILD_DIR}" || die
200         }
201
202         multibuild_foreach_variant _multibuild_create_source_copy
203 }
204
205 # @FUNCTION: run_in_build_dir
206 # @USAGE: <argv>...
207 # @DESCRIPTION:
208 # Run the given command in the directory pointed by BUILD_DIR.
209 run_in_build_dir() {
210         debug-print-function ${FUNCNAME} "${@}"
211         local ret
212
213         [[ ${#} -ne 0 ]] || die "${FUNCNAME}: no command specified."
214         [[ ${BUILD_DIR} ]] || die "${FUNCNAME}: BUILD_DIR not set."
215
216         mkdir -p "${BUILD_DIR}" || die
217         pushd "${BUILD_DIR}" >/dev/null || die
218         "${@}"
219         ret=${?}
220         popd >/dev/null || die
221
222         return ${ret}
223 }
224
225 # @FUNCTION: multibuild_merge_root
226 # @USAGE: <src-root> <dest-root>
227 # @DESCRIPTION:
228 # Merge the directory tree (fake root) from <src-root> to <dest-root>
229 # (the real root). Both directories have to be real, absolute paths
230 # (i.e. including ${D}). Source root will be removed.
231 multibuild_merge_root() {
232         local src=${1}
233         local dest=${2}
234
235         local ret
236
237         if use userland_BSD; then
238                 # Most of BSD variants fail to copy broken symlinks, #447370
239                 # also, they do not support --version
240
241                 tar -C "${src}" -f - -c . \
242                         | tar -x -f - -C "${dest}"
243                 [[ ${PIPESTATUS[*]} == '0 0' ]]
244                 ret=${?}
245         else
246                 local cp_args=()
247
248                 if cp -a --version &>/dev/null; then
249                         cp_args+=( -a )
250                 else
251                         cp_args+=( -P -R -p )
252                 fi
253
254                 if cp --reflink=auto --version &>/dev/null; then
255                         # enable reflinking if possible to make this faster
256                         cp_args+=( --reflink=auto )
257                 fi
258
259                 cp "${cp_args[@]}" "${src}"/. "${dest}"/
260                 ret=${?}
261         fi
262
263         if [[ ${ret} -ne 0 ]]; then
264                 die "${MULTIBUILD_VARIANT:-(unknown)}: merging image failed."
265         fi
266
267         rm -rf "${src}"
268 }
269
270 _MULTIBUILD=1
271 fi