Merge remote-tracking branch 'github/pr/550'.
[gentoo.git] / eclass / check-reqs.eclass
1 # Copyright 1999-2014 Gentoo Foundation
2 # Distributed under the terms of the GNU General Public License v2
3 # $Id$
4
5 # @ECLASS: check-reqs.eclass
6 # @MAINTAINER:
7 # QA Team <qa@gentoo.org>
8 # @AUTHOR:
9 # Bo Ørsted Andresen <zlin@gentoo.org>
10 # Original Author: Ciaran McCreesh <ciaranm@gentoo.org>
11 # @BLURB: Provides a uniform way of handling ebuild which have very high build requirements
12 # @DESCRIPTION:
13 # This eclass provides a uniform way of handling ebuilds which have very high
14 # build requirements in terms of memory or disk space. It provides a function
15 # which should usually be called during pkg_setup().
16 #
17 # The chosen action only happens when the system's resources are detected
18 # correctly and only if they are below the threshold specified by the package.
19 #
20 # @CODE
21 # # need this much memory (does *not* check swap)
22 # CHECKREQS_MEMORY="256M"
23 #
24 # # need this much temporary build space
25 # CHECKREQS_DISK_BUILD="2G"
26 #
27 # # install will need this much space in /usr
28 # CHECKREQS_DISK_USR="1G"
29 #
30 # # install will need this much space in /var
31 # CHECKREQS_DISK_VAR="1024M"
32 #
33 # @CODE
34 #
35 # If you don't specify a value for, say, CHECKREQS_MEMORY, then the test is not
36 # carried out.
37 #
38 # These checks should probably mostly work on non-Linux, and they should
39 # probably degrade gracefully if they don't. Probably.
40
41 if [[ ! ${_CHECK_REQS_ECLASS_} ]]; then
42
43 inherit eutils
44
45 # @ECLASS-VARIABLE: CHECKREQS_MEMORY
46 # @DEFAULT_UNSET
47 # @DESCRIPTION:
48 # How much RAM is needed? Eg.: CHECKREQS_MEMORY=15M
49
50 # @ECLASS-VARIABLE:  CHECKREQS_DISK_BUILD
51 # @DEFAULT_UNSET
52 # @DESCRIPTION:
53 # How much diskspace is needed to build the package? Eg.: CHECKREQS_DISK_BUILD=2T
54
55 # @ECLASS-VARIABLE: CHECKREQS_DISK_USR
56 # @DEFAULT_UNSET
57 # @DESCRIPTION:
58 # How much space in /usr is needed to install the package? Eg.: CHECKREQS_DISK_USR=15G
59
60 # @ECLASS-VARIABLE: CHECKREQS_DISK_VAR
61 # @DEFAULT_UNSET
62 # @DESCRIPTION:
63 # How much space is needed in /var? Eg.: CHECKREQS_DISK_VAR=3000M
64
65 EXPORT_FUNCTIONS pkg_setup
66 case "${EAPI:-0}" in
67         0|1|2|3) ;;
68         4|5|6) EXPORT_FUNCTIONS pkg_pretend ;;
69         *) die "EAPI=${EAPI} is not supported" ;;
70 esac
71
72 # @FUNCTION: check_reqs
73 # @DESCRIPTION:
74 # Obsolete function executing all the checks and printing out results
75 check_reqs() {
76         debug-print-function ${FUNCNAME} "$@"
77
78         [[ ${EAPI:-0} == [012345] ]] || die "${FUNCNAME} is banned in EAPI > 5"
79
80         echo
81         eqawarn "Package calling old ${FUNCNAME} function."
82         eqawarn "Please file a bug against the package."
83         eqawarn "It should call check-reqs_pkg_pretend and check-reqs_pkg_setup"
84         eqawarn "and possibly use EAPI=4 or later."
85         echo
86
87         check-reqs_pkg_setup "$@"
88 }
89
90 # @FUNCTION: check-reqs_pkg_setup
91 # @DESCRIPTION:
92 # Exported function running the resources checks in pkg_setup phase.
93 # It should be run in both phases to ensure condition changes between
94 # pkg_pretend and pkg_setup won't affect the build.
95 check-reqs_pkg_setup() {
96         debug-print-function ${FUNCNAME} "$@"
97
98         check-reqs_prepare
99         check-reqs_run
100         check-reqs_output
101 }
102
103 # @FUNCTION: check-reqs_pkg_pretend
104 # @DESCRIPTION:
105 # Exported function running the resources checks in pkg_pretend phase.
106 check-reqs_pkg_pretend() {
107         debug-print-function ${FUNCNAME} "$@"
108
109         check-reqs_pkg_setup "$@"
110 }
111
112 # @FUNCTION: check-reqs_prepare
113 # @INTERNAL
114 # @DESCRIPTION:
115 # Internal function that checks the variables that should be defined.
116 check-reqs_prepare() {
117         debug-print-function ${FUNCNAME} "$@"
118
119         if [[ -z ${CHECKREQS_MEMORY} &&
120                         -z ${CHECKREQS_DISK_BUILD} &&
121                         -z ${CHECKREQS_DISK_USR} &&
122                         -z ${CHECKREQS_DISK_VAR} ]]; then
123                 eerror "Set some check-reqs eclass variables if you want to use it."
124                 eerror "If you are user and see this message file a bug against the package."
125                 die "${FUNCNAME}: check-reqs eclass called but not actualy used!"
126         fi
127 }
128
129 # @FUNCTION: check-reqs_run
130 # @INTERNAL
131 # @DESCRIPTION:
132 # Internal function that runs the check based on variable settings.
133 check-reqs_run() {
134         debug-print-function ${FUNCNAME} "$@"
135
136         # some people are *censored*
137         unset CHECKREQS_FAILED
138
139         [[ ${EAPI:-0} == [0123] ]] && local MERGE_TYPE=""
140
141         # use != in test, because MERGE_TYPE only exists in EAPI 4 and later
142         if [[ ${MERGE_TYPE} != binary ]]; then
143                 [[ -n ${CHECKREQS_MEMORY} ]] && \
144                         check-reqs_memory \
145                                 ${CHECKREQS_MEMORY}
146
147                 [[ -n ${CHECKREQS_DISK_BUILD} ]] && \
148                         check-reqs_disk \
149                                 "${T}" \
150                                 "${CHECKREQS_DISK_BUILD}"
151         fi
152
153         if [[ ${MERGE_TYPE} != buildonly ]]; then
154                 [[ -n ${CHECKREQS_DISK_USR} ]] && \
155                         check-reqs_disk \
156                                 "${EROOT}/usr" \
157                                 "${CHECKREQS_DISK_USR}"
158
159                 [[ -n ${CHECKREQS_DISK_VAR} ]] && \
160                         check-reqs_disk \
161                                 "${EROOT}/var" \
162                                 "${CHECKREQS_DISK_VAR}"
163         fi
164 }
165
166 # @FUNCTION: check-reqs_get_kibibytes
167 # @INTERNAL
168 # @DESCRIPTION:
169 # Internal function that returns number in KiB.
170 # Returns 1024**2 for 1G or 1024**3 for 1T.
171 check-reqs_get_kibibytes() {
172         debug-print-function ${FUNCNAME} "$@"
173
174         [[ -z ${1} ]] && die "Usage: ${FUNCNAME} [size]"
175
176         local unit=${1:(-1)}
177         local size=${1%[GMT]}
178
179         case ${unit} in
180                 G) echo $((1024 * 1024 * size)) ;;
181                 M) echo $((1024 * size)) ;;
182                 T) echo $((1024 * 1024 * 1024 * size)) ;;
183                 [0-9]) echo $((1024 * size)) ;;
184                 *)
185                         die "${FUNCNAME}: Unknown unit: ${unit}"
186                 ;;
187         esac
188 }
189
190 # @FUNCTION: check-reqs_get_number
191 # @INTERNAL
192 # @DESCRIPTION:
193 # Internal function that returns the numerical value without the unit.
194 # Returns "1" for "1G" or "150" for "150T".
195 check-reqs_get_number() {
196         debug-print-function ${FUNCNAME} "$@"
197
198         [[ -z ${1} ]] && die "Usage: ${FUNCNAME} [size]"
199
200         local unit=${1:(-1)}
201         local size=${1%[GMT]}
202         local msg=eerror
203         [[ ${EAPI:-0} == [012345] ]] && msg=eqawarn
204
205         # Check for unset units and warn about them.
206         # Backcompat.
207         if [[ ${size} == ${1} ]]; then
208                 ${msg} "Package does not specify unit for the size check"
209                 ${msg} "File bug against the package. It should specify the unit."
210         fi
211
212         echo ${size}
213 }
214
215 # @FUNCTION: check-reqs_get_unit
216 # @INTERNAL
217 # @DESCRIPTION:
218 # Internal function that return the unit without the numerical value.
219 # Returns "GiB" for "1G" or "TiB" for "150T".
220 check-reqs_get_unit() {
221         debug-print-function ${FUNCNAME} "$@"
222
223         [[ -z ${1} ]] && die "Usage: ${FUNCNAME} [size]"
224
225         local unit=${1:(-1)}
226
227         case ${unit} in
228                 G) echo "GiB" ;;
229                 [M0-9]) echo "MiB" ;;
230                 T) echo "TiB" ;;
231                 *)
232                         die "${FUNCNAME}: Unknown unit: ${unit}"
233                 ;;
234         esac
235 }
236
237 # @FUNCTION: check-reqs_output
238 # @INTERNAL
239 # @DESCRIPTION:
240 # Internal function that prints the warning and dies if required based on
241 # the test results.
242 check-reqs_output() {
243         debug-print-function ${FUNCNAME} "$@"
244
245         local msg="ewarn"
246
247         [[ ${EBUILD_PHASE} == "pretend" && -z ${I_KNOW_WHAT_I_AM_DOING} ]] && msg="eerror"
248         if [[ -n ${CHECKREQS_FAILED} ]]; then
249                 ${msg}
250                 ${msg} "Space constraints set in the ebuild were not met!"
251                 ${msg} "The build will most probably fail, you should enhance the space"
252                 ${msg} "as per failed tests."
253                 ${msg}
254
255                 [[ ${EBUILD_PHASE} == "pretend" && -z ${I_KNOW_WHAT_I_AM_DOING} ]] && \
256                         die "Build requirements not met!"
257         fi
258 }
259
260 # @FUNCTION: check-reqs_memory
261 # @INTERNAL
262 # @DESCRIPTION:
263 # Internal function that checks size of RAM.
264 check-reqs_memory() {
265         debug-print-function ${FUNCNAME} "$@"
266
267         [[ -z ${1} ]] && die "Usage: ${FUNCNAME} [size]"
268
269         local size=${1}
270         local actual_memory
271
272         check-reqs_start_phase \
273                 ${size} \
274                 "RAM"
275
276         if [[ -r /proc/meminfo ]] ; then
277                 actual_memory=$(awk '/MemTotal/ { print $2 }' /proc/meminfo)
278         else
279                 actual_memory=$(sysctl hw.physmem 2>/dev/null )
280                 [[ "$?" == "0" ]] &&
281                         actual_memory=$(echo $actual_memory | sed -e 's/^[^:=]*[:=]//' )
282         fi
283         if [[ -n ${actual_memory} ]] ; then
284                 if [[ ${actual_memory} -lt $(check-reqs_get_kibibytes ${size}) ]] ; then
285                         eend 1
286                         check-reqs_unsatisfied \
287                                 ${size} \
288                                 "RAM"
289                 else
290                         eend 0
291                 fi
292         else
293                 eend 1
294                 ewarn "Couldn't determine amount of memory, skipping..."
295         fi
296 }
297
298 # @FUNCTION: check-reqs_disk
299 # @INTERNAL
300 # @DESCRIPTION:
301 # Internal function that checks space on the harddrive.
302 check-reqs_disk() {
303         debug-print-function ${FUNCNAME} "$@"
304
305         [[ -z ${2} ]] && die "Usage: ${FUNCNAME} [path] [size]"
306
307         local path=${1}
308         local size=${2}
309         local space_kbi
310
311         check-reqs_start_phase \
312                 ${size} \
313                 "disk space at \"${path}\""
314
315         space_kbi=$(df -Pk "${1}" 2>/dev/null | awk 'FNR == 2 {print $4}')
316
317         if [[ $? == 0 && -n ${space_kbi} ]] ; then
318                 if [[ ${space_kbi} -lt $(check-reqs_get_kibibytes ${size}) ]] ; then
319                         eend 1
320                         check-reqs_unsatisfied \
321                                 ${size} \
322                                 "disk space at \"${path}\""
323                 else
324                         eend 0
325                 fi
326         else
327                 eend 1
328                 ewarn "Couldn't determine disk space, skipping..."
329         fi
330 }
331
332 # @FUNCTION: check-reqs_start_phase
333 # @INTERNAL
334 # @DESCRIPTION:
335 # Internal function that inform about started check
336 check-reqs_start_phase() {
337         debug-print-function ${FUNCNAME} "$@"
338
339         [[ -z ${2} ]] && die "Usage: ${FUNCNAME} [size] [location]"
340
341         local size=${1}
342         local location=${2}
343         local sizeunit="$(check-reqs_get_number ${size}) $(check-reqs_get_unit ${size})"
344
345         ebegin "Checking for at least ${sizeunit} ${location}"
346 }
347
348 # @FUNCTION: check-reqs_unsatisfied
349 # @INTERNAL
350 # @DESCRIPTION:
351 # Internal function that inform about check result.
352 # It has different output between pretend and setup phase,
353 # where in pretend phase it is fatal.
354 check-reqs_unsatisfied() {
355         debug-print-function ${FUNCNAME} "$@"
356
357         [[ -z ${2} ]] && die "Usage: ${FUNCNAME} [size] [location]"
358
359         local msg="ewarn"
360         local size=${1}
361         local location=${2}
362         local sizeunit="$(check-reqs_get_number ${size}) $(check-reqs_get_unit ${size})"
363
364         [[ ${EBUILD_PHASE} == "pretend" && -z ${I_KNOW_WHAT_I_AM_DOING} ]] && msg="eerror"
365         ${msg} "There is NOT at least ${sizeunit} ${location}"
366
367         # @ECLASS-VARIABLE: CHECKREQS_FAILED
368         # @DESCRIPTION:
369         # @INTERNAL
370         # If set the checks failed and eclass should abort the build.
371         # Internal, do not set yourself.
372         CHECKREQS_FAILED="true"
373 }
374
375 _CHECK_REQS_ECLASS_=1
376 fi