summaryrefslogtreecommitdiff
path: root/src/chroot-tools/libremakepkg
blob: 00eb2e0a42191a887685aa3f2d91efe07ce3b16f (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
#!/usr/bin/env bash
set -euE
# libremakepkg

# Copyright (C) 2010-2012 Nicolás Reynolds <fauno@parabola.nu>
# Copyright (C) 2010-2012 Joshua Ismael Haase Hernández (xihh) <hahj87@gmail.com>
# Copyright (C) 2012 Michał Masłowski <mtjm@mtjm.eu>
# Copyright (C) 2012-2015, 2017-2018 Luke Shumaker <lukeshu@parabola.nu>
#
# License: GNU GPLv2+
#
# This file is part of Parabola.
#
# Parabola is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# Parabola is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Parabola. If not, see <http://www.gnu.org/licenses/>.

. "$(librelib conf)"
. "$(librelib messages)"
. "$(librelib chroot/makechrootpkg)"

set -o pipefail
shopt -s nullglob
umask 0022

# Global variables:
readonly _indent="$(librelib chroot/indent)"
readonly INCHROOT=$([[ -f /.arch-chroot ]] && echo true || echo false)
NONET=true # can be changed with the -N flag
# {PKG,SRC,SRCPKG,LOG}DEST set at runtime by makepkg.conf
# MAKEFLAGS, PACKAGER set at runtime by makepkg.conf
# LIBREUSER, LIBREHOME are set by conf.sh
librechroot_flags=()

# Hooks ########################################################################

hook_pre_build=(:)
hook_post_build=(:)
hook_check_pkgbuild=(:)
hook_check_pkg=(:)
. "$(librelib chroot/hooks-chcleanup.sh)"
. "$(librelib chroot/hooks-check.sh)"
. "$(librelib chroot/hooks-distcc.sh)"

# Boring/mundane functions #####################################################

indent() {
	"$_indent" ' |  '
}

# Usage: exit_copy $copydir $src_owner
# End immediately, but copy log files out
exit_copy() {
	local copydir=$1
	local src_owner=$2
	if ! $INCHROOT; then
		msg "Copying log and package files out of the chroot..."
		move_products "$copydir" "$src_owner"
	fi
}

# Usage; run_hook $hookname $args...
run_hook() {
	local hookname=$1; shift
	local hookvar="hook_${hookname}[@]"

	local fails=()

	for hook in "${!hookvar}"; do
		# The "& wait $!" trick is to prevent "||" from
		# disabling "set -e"
		{ "$hook" "$@" |& indent; } &
		wait $! || fails+=("$hook")
	done

	if [[ ${#fails[@]} -gt 0 ]]; then
		error "Failure(s) in %s: %s" "$hookname" "${fails[*]}"
		return $EXIT_FAILURE
	else
		return $EXIT_SUCCESS
	fi
}

# Usage: add_to_local_repo $copydir $pkgfiles...
add_to_local_repo() {
	local copydir=$1; shift
	mkdir -p "$copydir/repo"
	local pkgfile
	for pkgfile in "$@"; do
		cp "$pkgfile" "$copydir/repo"
		pushd "$copydir/repo" >/dev/null
		repo-add repo.db.tar.gz "${pkgfile##*/}"
		popd >/dev/null
	done
}

hook_post_build+=('chroot_cleanup')
chroot_cleanup() {
	local copydir=$1
	rm -f -- "$copydir"/chrootbuild
}

build() (
	local copydir=$1
	local srcpkg=$2
	local repack=$3
	local makepkg_args=("${@:4}")

	local startdir
	startdir=$(mktemp -d)
	chown "$LIBREUSER:" "$startdir"
	trap "rm -rf -- ${startdir@Q}" EXIT
	sudo -u "$LIBREUSER" bsdtar -xf "$srcpkg" -C "$startdir" --strip-components 1

	local run_ynet=()
	local run_nnet=()
	if $INCHROOT; then
		local _run=(sh -c "mount --bind -o ro -- ${startdir@Q} ${startdir@Q} && cd ${startdir@Q} && \$@" --)
		run_ynet=(unshare --mount       -- "${_run[@]}")
		run_nnet=(unshare --mount --net -- "${_run[@]}")
	else
		librechroot_flags+=(-r "$startdir:/startdir")
		run_ynet=(librechroot "${librechroot_flags[@]}"    run)
		run_nnet=(librechroot "${librechroot_flags[@]}" -N run)
	fi
	$NONET || run_nnet=("${run_ynet[@]}")

	prepare_chroot "$copydir" "$LIBREHOME" "$repack" false
	run_hook pre_build "$copydir"
	trap "run_hook post_build ${copydir@Q}; rm -rf -- ${startdir@Q}" EXIT
	"${run_nnet[@]}" /chrootbuild "${makepkg_args[@]}" </dev/null |& indent
)

# The main program #############################################################

usage() {
	print "Usage: %s [options]" "${0##*/}"
	print 'This program will build your package.'
	echo
	prose 'If run from outside of a chroot, this command will make the
	       following configuration changes in the chroot:'
	bullet 'whatever changes `librechroot` makes.'
	bullet 'set `{PKG,SRC,SRCPKG,LOG}DEST` in `/etc/makepkg.conf`'
	bullet 'set `MAKEFLAGS` and `PACKAGER` in `/etc/makepkg.conf` to reflect
	        the value outside of the chroot.'
	bullet 'create a `builduser` with the same numeric UID as the
	        invoking $SUDO_USER.'
	bullet 'let this `builduser` call `sudo pacman` without a password.'
	bullet 'set `keyserver-options` in `~builduser/.gnupg/gpg.conf`'
	bullet "copy the user's GnuPG pubring to be the \`builduser\`'s pubring"
	bullet 'add a pacman repositoriy of locally built packages'
	echo
	prose 'This command is configured both with `chroot.conf` (either in
	       `/etc/libretools.d/` or `$XDG_CONFIG_HOME/libretools/`), and with
	       makepkg.conf(5).  However, similarly to makepkg(8), it lets
	       environment variables for {SRC,SRCPKG,PKG,LOG}DEST, MAKEFLAGS and
	       PACKAGER override the settings in makepkg.conf(5).'
	echo
	prose 'The `-n` and `-l` options behave identically to librechroot, see
	       the documentation there.'
	echo
	print 'Options:'
	print ' %s options:' librechroot
	flag "-n <$(_ CHROOT)>" 'Name of the chroot to use'
	flag "-l <$(_ COPY)>"   'Name of, or absolute path to, the chroot copy to use'
	flag "-w <$(_ 'PATH[:INSIDE_PATH[:OPTIONS]]')>" 'Bind mount a file or directory, read/write'
	flag "-r <$(_ 'PATH[:INSIDE_PATH[:OPTIONS]]')>" 'Bind mount a file or directory, read-only'
	print ' %s options:' libremakepkg
	flag '-N'               "Don't disable networking during prepare(),
	                         build(), and package().  PLEASE don't use this
	                         unless you have a special reason, its use is a
	                         violation of Parabola policy."
	flag '-R'               'Repackage contents of the package without rebuilding'
	flag '-h'               'Show this message'
}

# Convenience method for use in option parsing
err_chflag() {
	local flag=$1
	error 'The -%s flag does not make sense inside of a chroot' "$flag"
	exit $EXIT_INVALIDARGUMENT
}

main() {
	# Initial variable values ##############################################
	local copy=$([[ $LIBREUSER == root ]] && echo copy || echo "$LIBREUSER")
	local makepkg_args=(--syncdeps --noconfirm --log --holdver --skipinteg)
	local repack=false
	local chroot=''

	# Parse command line options ###########################################
	while getopts 'n:l:w:r:NRh' flag ; do
		case "${flag}" in
			n) if $INCHROOT; then err_chflag "$flag"; else
				chroot=$OPTARG; fi;;
			l) if $INCHROOT; then err_chflag "$flag"; else
				copy=$OPTARG; fi;;
			w|r) if $INCHROOT; then err_chflag "$flag"; else
				librechroot_flags+=(-$flag "$OPTARG"); fi;;
			N) NONET=false;;
			R) repack=true; makepkg_args+=(-R);;
			h) usage; exit $EXIT_SUCCESS;;
			*) usage >&2; exit $EXIT_INVALIDARGUMENT;;
		esac
	done
	shift $((OPTIND - 1))
	if [[ $# != 0 ]]; then
		error 'Extra arguments: %s' "$*"
		exit $EXIT_INVALIDARGUMENT
	fi

	# Resolve the chroot path ##############################################
	local copydir
	if $INCHROOT; then
		copydir='/'
	else
		load_conf chroot.conf CHROOTDIR CHROOT || exit
		[[ -z ${chroot} ]] || CHROOT=$chroot
		if [[ ${copy:0:1} = / ]]; then
			copydir=$copy
		else
			copydir="${CHROOTDIR}/${CHROOT}/${copy}"
		fi
		unset CHROOTDIR CHROOTEXTRAPKG
	fi
	unset chroot

	# Load makepkg configuration ###########################################
	# Note that all of these are globals
	PKGDEST="$(get_var makepkg PKGDEST "$PWD")"
	SRCDEST="$(get_var makepkg SRCDEST "$PWD")"
	SRCPKGDEST="$(get_var makepkg SRCPKGDEST "$PWD")"
	LOGDEST="$(get_var makepkg LOGDEST "$PWD")"
	MAKEFLAGS="$(get_var makepkg MAKEFLAGS '')"
	PACKAGER="$(get_var makepkg PACKAGER '')"

	# Quick sanity check ###################################################

	if (( EUID )); then
		error "This program must be run as root"
		exit $EXIT_NOPERMISSION
	fi

	if [[ ! -f PKGBUILD ]]; then
		# This is the message used by makepkg
		error "PKGBUILD does not exist."
		exit $EXIT_FAILURE
	fi

	# Make sure that the various *DEST directories exist
	sudo -u "$LIBREUSER" mkdir -p -- "$PKGDEST" "$SRCDEST" "$SRCPKGDEST" "$LOGDEST"

	# OK, we are starting now ##############################################

	if $INCHROOT; then
		lock 9 "/build/.lock" \
			"Waiting for existing lock on build directory to be released"
	else
		librechroot_flags+=(
			-n "$CHROOT"
			-l "$copy"
		)

		# Obtain a lock on the chroot
		lock 9 "$copydir.lock" \
			"Waiting for existing lock on chroot copy to be released: [%s]" "$copy"
		# Create the chroot if it does not exist
		msg 'Initializing the chroot...'
		librechroot "${librechroot_flags[@]}" make |& indent
	fi

	# Set target CARCH
	# note that we waited until after locking/creating the chroot to do this
	CARCH="$(MAKEPKG_CONF=$copydir/etc/makepkg.conf get_var makepkg CARCH)"
	export CARCH

	# Pre-build
	msg 'Starting pre-build activities...'
	run_hook check_pkgbuild

	msg 'Downloading sources...'
	local srcpkgdest srcpkg
	srcpkgdest="$(mktemp -d)"
	chown "$LIBREUSER:" "$srcpkgdest"
	trap "rm -rf -- ${srcpkgdest@Q}" EXIT
	SRCPKGDEST="$srcpkgdest" download_sources "$copydir" "$LIBREUSER" |& indent
	srcpkg=("$srcpkgdest"/*)
	if (( ${#srcpkg[@]} != 1 )); then
		error 'Something went funny with makepkg --allsource'
		return $EXIT_FAILURE
	fi
	# We want to inject "-$pkgarch" in to srcpkg's filename, right before $SRCEXT
	local srcext pkgarch srcpkg_filename
	srcext="$(MAKEPKG_CONF=$copydir/etc/makepkg.conf get_var makepkg SRCEXT)"
	if [[ "$(bsdtar xfO "$srcpkg" --include='*/.SRCINFO' | grep $'\tarch =')" = $'\tarch = any' ]]; then
		pkgarch=any
	else
		pkgarch=$CARCH
	fi
	srcpkg_filename=${srcpkg##*/}
	srcpkg_filename=${srcpkg_filename%"${srcext}"}-${pkgarch}${srcext}
	mv -T -- "$srcpkg" "$SRCPKGDEST/${srcpkg_filename}"
	srcpkg="$SRCPKGDEST/${srcpkg_filename}"
	rmdir -- "${srcpkgdest}"
	trap EXIT

	# Build
	msg 'Starting to build the package...'
	trap "exit_copy '$copydir' '$LIBREUSER'" EXIT
	build "$copydir" "$srcpkg" "$repack" "${makepkg_args[@]}"

	# Post-build
	msg 'Starting post-build activities...'
	run_hook check_pkg
	add_to_local_repo "$copydir" "$copydir"/pkgdest/*.pkg.tar* |& indent
}

main "$@"