summaryrefslogtreecommitdiff
blob: 5c4a420ee06e86e569b5b81fc718bcb9dbd37586 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
# Copyright 2004-2021 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2

# @ECLASS: check-reqs.eclass
# @MAINTAINER:
# QA Team <qa@gentoo.org>
# @AUTHOR:
# Bo Ørsted Andresen <zlin@gentoo.org>
# Original Author: Ciaran McCreesh <ciaranm@gentoo.org>
# @SUPPORTED_EAPIS: 4 5 6 7
# @BLURB: Provides a uniform way of handling ebuild which have very high build requirements
# @DESCRIPTION:
# This eclass provides a uniform way of handling ebuilds which have very high
# build requirements in terms of memory or disk space. It provides a function
# which should usually be called during pkg_setup().
#
# The chosen action only happens when the system's resources are detected
# correctly and only if they are below the threshold specified by the package.
#
# @CODE
# # need this much memory (does *not* check swap)
# CHECKREQS_MEMORY="256M"
#
# # need this much temporary build space
# CHECKREQS_DISK_BUILD="2G"
#
# # install will need this much space in /usr
# CHECKREQS_DISK_USR="1G"
#
# # install will need this much space in /var
# CHECKREQS_DISK_VAR="1024M"
#
# @CODE
#
# If you don't specify a value for, say, CHECKREQS_MEMORY, then the test is not
# carried out.
#
# These checks should probably mostly work on non-Linux, and they should
# probably degrade gracefully if they don't. Probably.

if [[ ! ${_CHECK_REQS_ECLASS_} ]]; then

# @ECLASS-VARIABLE: CHECKREQS_MEMORY
# @DEFAULT_UNSET
# @DESCRIPTION:
# How much RAM is needed? Eg.: CHECKREQS_MEMORY=15M

# @ECLASS-VARIABLE:  CHECKREQS_DISK_BUILD
# @DEFAULT_UNSET
# @DESCRIPTION:
# How much diskspace is needed to build the package? Eg.: CHECKREQS_DISK_BUILD=2T

# @ECLASS-VARIABLE: CHECKREQS_DISK_USR
# @DEFAULT_UNSET
# @DESCRIPTION:
# How much space in /usr is needed to install the package? Eg.: CHECKREQS_DISK_USR=15G

# @ECLASS-VARIABLE: CHECKREQS_DISK_VAR
# @DEFAULT_UNSET
# @DESCRIPTION:
# How much space is needed in /var? Eg.: CHECKREQS_DISK_VAR=3000M

case ${EAPI:-0} in
	4|5|6|7) ;;
	*) die "${ECLASS}: EAPI=${EAPI:-0} is not supported" ;;
esac

EXPORT_FUNCTIONS pkg_pretend pkg_setup

# Obsolete function executing all the checks and printing out results
check_reqs() {
	eerror "Package calling old ${FUNCNAME} function."
	eerror "It should call check-reqs_pkg_pretend and check-reqs_pkg_setup."
	die "${FUNCNAME} is banned"
}

# @FUNCTION: check-reqs_pkg_setup
# @DESCRIPTION:
# Exported function running the resources checks in pkg_setup phase.
# It should be run in both phases to ensure condition changes between
# pkg_pretend and pkg_setup won't affect the build.
check-reqs_pkg_setup() {
	debug-print-function ${FUNCNAME} "$@"

	check-reqs_prepare
	check-reqs_run
	check-reqs_output
}

# @FUNCTION: check-reqs_pkg_pretend
# @DESCRIPTION:
# Exported function running the resources checks in pkg_pretend phase.
check-reqs_pkg_pretend() {
	debug-print-function ${FUNCNAME} "$@"

	check-reqs_pkg_setup "$@"
}

# @FUNCTION: check-reqs_prepare
# @INTERNAL
# @DESCRIPTION:
# Internal function that checks the variables that should be defined.
check-reqs_prepare() {
	debug-print-function ${FUNCNAME} "$@"

	if [[ -z ${CHECKREQS_MEMORY} &&
			-z ${CHECKREQS_DISK_BUILD} &&
			-z ${CHECKREQS_DISK_USR} &&
			-z ${CHECKREQS_DISK_VAR} ]]; then
		eerror "Set some check-reqs eclass variables if you want to use it."
		eerror "If you are user and see this message file a bug against the package."
		die "${FUNCNAME}: check-reqs eclass called but not actually used!"
	fi
}

# @FUNCTION: check-reqs_run
# @INTERNAL
# @DESCRIPTION:
# Internal function that runs the check based on variable settings.
check-reqs_run() {
	debug-print-function ${FUNCNAME} "$@"

	# some people are *censored*
	unset CHECKREQS_FAILED

	if [[ ${MERGE_TYPE} != binary ]]; then
		[[ -n ${CHECKREQS_MEMORY} ]] && \
			check-reqs_memory \
				${CHECKREQS_MEMORY}

		[[ -n ${CHECKREQS_DISK_BUILD} ]] && \
			check-reqs_disk \
				"${T}" \
				"${CHECKREQS_DISK_BUILD}"
	fi

	if [[ ${MERGE_TYPE} != buildonly ]]; then
		[[ -n ${CHECKREQS_DISK_USR} ]] && \
			check-reqs_disk \
				"${EROOT%/}/usr" \
				"${CHECKREQS_DISK_USR}"

		[[ -n ${CHECKREQS_DISK_VAR} ]] && \
			check-reqs_disk \
				"${EROOT%/}/var" \
				"${CHECKREQS_DISK_VAR}"
	fi
}

# @FUNCTION: check-reqs_get_kibibytes
# @INTERNAL
# @DESCRIPTION:
# Internal function that returns number in KiB.
# Returns 1024**2 for 1G or 1024**3 for 1T.
check-reqs_get_kibibytes() {
	debug-print-function ${FUNCNAME} "$@"

	[[ -z ${1} ]] && die "Usage: ${FUNCNAME} [size]"

	local unit=${1:(-1)}
	local size=${1%[GMT]}

	case ${unit} in
		M) echo $((1024 * size)) ;;
		G) echo $((1024 * 1024 * size)) ;;
		T) echo $((1024 * 1024 * 1024 * size)) ;;
		*)
			die "${FUNCNAME}: Unknown unit: ${unit}"
		;;
	esac
}

# @FUNCTION: check-reqs_get_number
# @INTERNAL
# @DESCRIPTION:
# Internal function that returns the numerical value without the unit.
# Returns "1" for "1G" or "150" for "150T".
check-reqs_get_number() {
	debug-print-function ${FUNCNAME} "$@"

	[[ -z ${1} ]] && die "Usage: ${FUNCNAME} [size]"

	local size=${1%[GMT]}
	[[ ${size} == ${1} ]] && die "${FUNCNAME}: Missing unit: ${1}"

	echo ${size}
}

# @FUNCTION: check-reqs_get_unit
# @INTERNAL
# @DESCRIPTION:
# Internal function that returns the unit without the numerical value.
# Returns "GiB" for "1G" or "TiB" for "150T".
check-reqs_get_unit() {
	debug-print-function ${FUNCNAME} "$@"

	[[ -z ${1} ]] && die "Usage: ${FUNCNAME} [size]"

	local unit=${1:(-1)}

	case ${unit} in
		M) echo "MiB" ;;
		G) echo "GiB" ;;
		T) echo "TiB" ;;
		*)
			die "${FUNCNAME}: Unknown unit: ${unit}"
		;;
	esac
}

# @FUNCTION: check-reqs_output
# @INTERNAL
# @DESCRIPTION:
# Internal function that prints the warning and dies if required based on
# the test results.
check-reqs_output() {
	debug-print-function ${FUNCNAME} "$@"

	local msg="ewarn"

	[[ ${EBUILD_PHASE} == "pretend" && -z ${I_KNOW_WHAT_I_AM_DOING} ]] && msg="eerror"
	if [[ -n ${CHECKREQS_FAILED} ]]; then
		${msg}
		${msg} "Space constraints set in the ebuild were not met!"
		${msg} "The build will most probably fail, you should enhance the space"
		${msg} "as per failed tests."
		${msg}

		[[ ${EBUILD_PHASE} == "pretend" && -z ${I_KNOW_WHAT_I_AM_DOING} ]] && \
			die "Build requirements not met!"
	fi
}

# @FUNCTION: check-reqs_memory
# @INTERNAL
# @DESCRIPTION:
# Internal function that checks size of RAM.
check-reqs_memory() {
	debug-print-function ${FUNCNAME} "$@"

	[[ -z ${1} ]] && die "Usage: ${FUNCNAME} [size]"

	local size=${1}
	local actual_memory
	local actual_swap

	check-reqs_start_phase \
		${size} \
		"RAM"

	if [[ -r /proc/meminfo ]] ; then
		actual_memory=$(awk '/MemTotal/ { print $2 }' /proc/meminfo)
		actual_swap=$(awk '/SwapTotal/ { print $2 }' /proc/meminfo)
	else
		actual_memory=$(sysctl hw.physmem 2>/dev/null)
		[[ $? -eq 0 ]] && actual_memory=$(echo "${actual_memory}" \
			| sed -e 's/^[^:=]*[:=][[:space:]]*//')
		actual_swap=$(sysctl vm.swap_total 2>/dev/null)
		[[ $? -eq 0 ]] && actual_swap=$(echo "${actual_swap}" \
			| sed -e 's/^[^:=]*[:=][[:space:]]*//')
	fi
	if [[ -n ${actual_memory} ]] ; then
		if [[ ${actual_memory} -ge $(check-reqs_get_kibibytes ${size}) ]] ; then
			eend 0
		elif [[ -n ${actual_swap} && $((${actual_memory} + ${actual_swap})) \
				-ge $(check-reqs_get_kibibytes ${size}) ]] ; then
			ewarn "Amount of main memory is insufficient, but amount"
			ewarn "of main memory combined with swap is sufficient."
			ewarn "Build process may make computer very slow!"
			eend 0
		else
			eend 1
			check-reqs_unsatisfied \
				${size} \
				"RAM"
		fi
	else
		eend 1
		ewarn "Couldn't determine amount of memory, skipping..."
	fi
}

# @FUNCTION: check-reqs_disk
# @INTERNAL
# @DESCRIPTION:
# Internal function that checks space on the harddrive.
check-reqs_disk() {
	debug-print-function ${FUNCNAME} "$@"

	[[ -z ${2} ]] && die "Usage: ${FUNCNAME} [path] [size]"

	local path=${1}
	local size=${2}
	local space_kbi

	check-reqs_start_phase \
		${size} \
		"disk space at \"${path}\""

	space_kbi=$(df -Pk "${1}" 2>/dev/null | awk 'FNR == 2 {print $4}')

	if [[ $? == 0 && -n ${space_kbi} ]] ; then
		if [[ ${space_kbi} -lt $(check-reqs_get_kibibytes ${size}) ]] ; then
			eend 1
			check-reqs_unsatisfied \
				${size} \
				"disk space at \"${path}\""
		else
			eend 0
		fi
	else
		eend 1
		ewarn "Couldn't determine disk space, skipping..."
	fi
}

# @FUNCTION: check-reqs_start_phase
# @INTERNAL
# @DESCRIPTION:
# Internal function that inform about started check
check-reqs_start_phase() {
	debug-print-function ${FUNCNAME} "$@"

	[[ -z ${2} ]] && die "Usage: ${FUNCNAME} [size] [location]"

	local size=${1}
	local location=${2}
	local sizeunit="$(check-reqs_get_number ${size}) $(check-reqs_get_unit ${size})"

	ebegin "Checking for at least ${sizeunit} ${location}"
}

# @FUNCTION: check-reqs_unsatisfied
# @INTERNAL
# @DESCRIPTION:
# Internal function that inform about check result.
# It has different output between pretend and setup phase,
# where in pretend phase it is fatal.
check-reqs_unsatisfied() {
	debug-print-function ${FUNCNAME} "$@"

	[[ -z ${2} ]] && die "Usage: ${FUNCNAME} [size] [location]"

	local msg="ewarn"
	local size=${1}
	local location=${2}
	local sizeunit="$(check-reqs_get_number ${size}) $(check-reqs_get_unit ${size})"

	[[ ${EBUILD_PHASE} == "pretend" && -z ${I_KNOW_WHAT_I_AM_DOING} ]] && msg="eerror"
	${msg} "There is NOT at least ${sizeunit} ${location}"

	# @ECLASS-VARIABLE: CHECKREQS_FAILED
	# @INTERNAL
	# @DESCRIPTION:
	# If set the checks failed and eclass should abort the build.
	# Internal, do not set yourself.
	CHECKREQS_FAILED="true"
}

_CHECK_REQS_ECLASS_=1
fi