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

# Copyright (C) 2010-2011 Nicolás Reynolds
# Copyright (C) 2011 Joshua Ismael Haase Hernández
# Copyright (C) 2012-2014 Luke Shumaker
#
# 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 3 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.sh)

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: _check_perms_dir $directory
# Make sure that $directory is readable and executable (searchable) by 'nobody'
check_directory_permissions() (
	local dir=$1
	# `cd` to the directory, then test `.`; that way if parent
	# directories aren't readable, we aren't testing for that.  We
	# only need the last element in `$dir`.
	cd "$dir"
	if ! sudo -u nobody test -r . -a -x .; then
		error "Directory '%s' must be readable by user 'nobody'" "$dir"
		return 1
	fi
	return 0
)

# 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
		"$hook" "$@" || fails+=("$hook")
	done |& indent

	if [[ ${#fails[@]} -gt 0 ]]; then
		error "Failure(s) in %s: %s" "$hookname" "${fails[*]}"
		return 1
	else
		return 0
	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+=('cleanup')
cleanup() {
	local copydir=$1
	rm -f -- "$copydir"/chroot{prepare,build}
}

build() (
	local copydir=$1; shift
	local repack=$1; shift

	local netflag=''
	local run=()
	if $INCHROOT; then
		! $NONET || netflag='-N'
		run=(unshare)
	else
		! $NONET || netflag='-n'
		run=(librechroot "${librechroot_flags[@]}" run)
	fi

	prepare_chroot "$copydir" "$LIBREHOME" "$repack" false
	"${run[@]}" /chrootprepare "$@" |& indent
	run_hook pre_build "$copydir"
	trap "run_hook post_build '$copydir'" EXIT
	"${run[@]}" /chrootbuild "$@" |& indent
)

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

usage() {
	print "Usage: %s [options] [-- makepkg args]" "${0##*/}"
	print 'This program will build your package.'
	echo
	prose 'If run from outside of a chroot, 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 `PACKAGER` in `/etc/makepkg.conf` to reflect the value
	        outside of the chroot.'
	bullet '(maybe) delete `/build/.makepkg.conf`'
	bullet '(maybe) delete `/build/.ssh/config`'
	prose 'If run from inside of a chroot, this command will:'
	bullet '(maybe) delete `~/.makepkg.conf`'
	bullet '(maybe) delete `~/.ssh/config`'
	prose 'The above "maybe"s happen as part of the workarounds to make
	       distcc work in a network-less environment.  They will happen if
	       both `socat` and `distcc` are installed in the chroot.'
	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[:PATH]')>" 'Bind mount a file or directory, read/write'
	flag "-r <$(_ 'PATH[:PATH]')>" 'Bind mount a file or directory, read-only'
	print ' %s options:' libremakepkg
	flag '-N' 		"Don't disable networking during 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"
	return 1
}

main() {
	# Initial variable values ##############################################
	local copy=$([[ $LIBREUSER == root ]] && echo copy || echo "$LIBREUSER")
	local makepkg_args=(-s --noconfirm -L)
	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; return 0;;
			*) usage >&2; return 1;;
		esac
	done
	shift $(($OPTIND - 1))
	# Pass all arguments after -- right to makepkg
	makepkg_args+=("$@")

	# Resolve the chroot path ##############################################
	local copydir
	if $INCHROOT; then
		copydir='/'
	else
		load_files chroot
		check_vars chroot CHROOTDIR CHROOT
		[[ -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 1
	fi

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

	# Make sure that the various *DEST directories exist
	mkdir -p -- "$PKGDEST" "$SRCDEST" "$SRCPKGDEST" "$LOGDEST"
	# Check the permissions for $startdir and $SRCDEST
	(
		declare -i ret=0
		check_directory_permissions "$PWD" || ret=1
		if ! [[ "$PWD" -ef "$SRCDEST" ]]; then
			check_directory_permissions "$SRCDEST" || ret=1
		fi
		exit $ret
	)

	# 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+=(
			-r "$PWD:/startdir_host"
			-r "$SRCDEST:/srcdest_host"
			-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
	export CARCH="$(MAKEPKG_CONF=$copydir/etc/makepkg.conf get_var makepkg CARCH)"

	# Pre-build
	msg 'Starting pre-build activities...'
	run_hook check_pkgbuild
	msg 'Downloading sources...'
	download_sources "$copydir" "$LIBREUSER" |& indent

	# Build
	msg 'Starting to build the package...'
	trap "exit_copy '$copydir' '$LIBREUSER'" EXIT
	build "$copydir" "$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 "$@"