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