Newer
Older
# This program 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; version 2 of the License.
#
# This program 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.
makepkg_args=(-s --noconfirm -L --holdver)
repack=false
update_first=false
clean_first=false
install_pkg=
run_namcap=false
bindmounts_ro=()
bindmounts_rw=()
copy=$USER
[[ -n $SUDO_USER ]] && copy=$SUDO_USER
[[ -z "$copy" || $copy = root ]] && copy=copy
src_owner=${SUDO_USER:-$USER}
echo "Usage: ${0##*/} [options] -r <chrootdir> [--] [makepkg args]"
echo ' Run this script in a PKGBUILD dir to build a package inside a'
echo ' clean chroot. Arguments passed to this script after the'
echo ' end-of-options marker (--) will be passed to makepkg.'
echo ''
echo ' The chroot dir consists of the following directories:'
echo ' <chrootdir>/{root, copy} but only "root" is required'
echo ' by default. The working copy will be created as needed'
echo ''
echo 'The chroot "root" directory must be created via the following'
echo 'command:'
echo ' mkarchroot <chrootdir>/root base-devel'
echo "Default makepkg args: ${makepkg_args[*]}"
echo ''
echo 'Flags:'
echo '-h This help'
echo '-c Clean the chroot before building'
echo '-d <dir> Bind directory into build chroot as read-write'
echo '-D <dir> Bind directory into build chroot as read-only'
echo '-u Update the working copy of the chroot before building'
echo ' This is useful for rebuilds without dirtying the pristine'
echo ' chroot'
echo '-r <dir> The chroot dir to use'
echo '-I <pkg> Install a package into the working copy of the chroot'
echo '-l <copy> The directory to use as the working copy of the chroot'
echo ' Useful for maintaining multiple copies'
echo " Default: $copy"
echo '-n Run namcap on the package'
echo '-T Build in a temporary directory'
Jan Alexander Steffens (heftig)
committed
# {{{ functions
load_vars() {
local makepkg_conf="$1" var
Jan Alexander Steffens (heftig)
committed
[[ -f $makepkg_conf ]] || return 1
for var in {SRC,SRCPKG,PKG,LOG}DEST MAKEFLAGS PACKAGER; do
Jan Alexander Steffens (heftig)
committed
[[ -z ${!var} ]] && eval $(grep "^${var}=" "$makepkg_conf")
done
Jan Alexander Steffens (heftig)
committed
return 0
}
Jan Alexander Steffens (heftig)
committed
create_chroot() {
# Lock the chroot we want to use. We'll keep this lock until we exit.
lock 9 "$copydir.lock" "Locking chroot copy [$copy]"
if [[ ! -d $copydir ]] || $clean_first; then
# Get a read lock on the root chroot to make
# sure we don't clone a half-updated chroot
slock 8 "$chrootdir/root.lock" "Locking clean chroot"
stat_busy "Creating clean working copy [$copy]"
if [[ "$chroottype" == btrfs ]] && ! mountpoint -q "$copydir"; then
Jan Alexander Steffens (heftig)
committed
if [[ -d $copydir ]]; then
btrfs subvolume delete "$copydir" >/dev/null ||
die "Unable to delete subvolume %s" "$copydir"
Jan Alexander Steffens (heftig)
committed
fi
btrfs subvolume snapshot "$chrootdir/root" "$copydir" >/dev/null ||
die "Unable to create subvolume %s" "$copydir"
Jan Alexander Steffens (heftig)
committed
else
mkdir -p "$copydir"
rsync -a --delete -q -W -x "$chrootdir/root/" "$copydir"
fi
stat_done
Jan Alexander Steffens (heftig)
committed
# Drop the read lock again
exec 8>&-
fi
# Update mtime
touch "$copydir"
Jan Alexander Steffens (heftig)
committed
}
Jan Alexander Steffens (heftig)
committed
clean_temporary() {
stat_busy "Removing temporary copy [$copy]"
if [[ "$chroottype" == btrfs ]] && ! mountpoint -q "$copydir"; then
Jan Alexander Steffens (heftig)
committed
btrfs subvolume delete "$copydir" >/dev/null ||
die "Unable to delete subvolume %s" "$copydir"
Jan Alexander Steffens (heftig)
committed
# avoid change of filesystem in case of an umount failure
rm --recursive --force --one-file-system "$copydir" ||
die "Unable to delete %s" "$copydir"
Jan Alexander Steffens (heftig)
committed
# remove lock file
rm -f "$copydir.lock"
Jan Alexander Steffens (heftig)
committed
}
Jan Alexander Steffens (heftig)
committed
install_packages() {
local -a pkgnames
local ret
pkgnames=("${install_pkgs[@]##*/}")
cp -- "${install_pkgs[@]}" "$copydir/root/"
arch-nspawn "$copydir" "${bindmounts_ro[@]}" "${bindmounts_rw[@]}" \
pacman -U --noconfirm -- "${pkgnames[@]/#//root/}"
ret=$?
rm -- "${pkgnames[@]/#/$copydir/root/}"
Jan Alexander Steffens (heftig)
committed
# If there is no PKGBUILD we are done
Jan Alexander Steffens (heftig)
committed
}
Jan Alexander Steffens (heftig)
committed
prepare_chroot() {
$repack || rm -rf "$copydir/build"
local builduser_uid="${SUDO_UID:-$UID}"
local builduser_gid="$(id -g "$builduser_uid")"
local install="install -o $builduser_uid -g $builduser_gid"
local x
# We can't use useradd without chrooting, otherwise it invokes PAM modules
# which we might not be able to load (i.e. when building i686 packages on
# an x86_64 host).
sed -e '/^builduser:/d' -i "$copydir"/etc/{passwd,group}
printf >>"$copydir/etc/group" 'builduser:x:%d:\n' $builduser_gid
printf >>"$copydir/etc/passwd" 'builduser:x:%d:%d:builduser:/build:/bin/bash\n' $builduser_uid $builduser_gid
$install -d "$copydir"/{build,build/.gnupg,startdir,{pkg,srcpkg,src,log}dest}
for x in .gnupg/pubring.{kbx,gpg}; do
[[ -r $USER_HOME/$x ]] || continue
$install -m 644 "$USER_HOME/$x" "$copydir/build/$x"
done
sed -e '/^MAKEFLAGS=/d' -e '/^PACKAGER=/d' -i "$copydir/etc/makepkg.conf"
for x in BUILDDIR=/build PKGDEST=/pkgdest SRCPKGDEST=/srcpkgdest SRCDEST=/srcdest LOGDEST=/logdest \
"MAKEFLAGS='$MAKEFLAGS'" "PACKAGER='$PACKAGER'"
do
grep -q "^$x" "$copydir/etc/makepkg.conf" && continue
echo "$x" >>"$copydir/etc/makepkg.conf"
done
cat > "$copydir/etc/sudoers.d/builduser-pacman" <<EOF
Defaults env_keep += "HOME"
builduser ALL = NOPASSWD: /usr/bin/pacman
EOF
chmod 440 "$copydir/etc/sudoers.d/builduser-pacman"
Jan Alexander Steffens (heftig)
committed
# This is a little gross, but this way the script is recreated every time in the
# working copy
printf '#!/bin/bash\n'
declare -f _chrootbuild
printf '_chrootbuild'
printf ' %q' "${makepkg_args[@]}"
printf ' || exit\n'
if $run_namcap; then
declare -f _chrootnamcap
printf '_chrootnamcap || exit\n'
fi
} >"$copydir/chrootbuild"
Jan Alexander Steffens (heftig)
committed
chmod +x "$copydir/chrootbuild"
}
# These functions aren't run in makechrootpkg,
# so no global variables
_chrootbuild() {
. /etc/profile
export HOME=/build
cd /startdir
sudo -u builduser makepkg "$@"
}
_chrootnamcap() {
pacman -S --needed --noconfirm namcap
for pkgfile in /startdir/PKGBUILD /pkgdest/*; do
echo "Checking ${pkgfile##*/}"
sudo -u builduser namcap "$pkgfile" 2>&1 | tee "/logdest/${pkgfile##*/}-namcap.log"
done
}
Jan Alexander Steffens (heftig)
committed
download_sources() {
local builddir="$(mktemp -d)"
chmod 1777 "$builddir"
# Ensure sources are downloaded
if [[ -n $SUDO_USER ]]; then
sudo -u $SUDO_USER env SRCDEST="$SRCDEST" BUILDDIR="$builddir" \
makepkg --config="$copydir/etc/makepkg.conf" --verifysource -o
else
( export SRCDEST BUILDDIR="$builddir"
makepkg --asroot --config="$copydir/etc/makepkg.conf" --verifysource -o
)
fi
(( $? != 0 )) && die "Could not download sources."
# Clean up garbage from verifysource
rm -rf $builddir
}
Jan Alexander Steffens (heftig)
committed
move_products() {
for pkgfile in "$copydir"/pkgdest/*; do
chown "$src_owner" "$pkgfile"
mv "$pkgfile" "$PKGDEST"
Jan Alexander Steffens (heftig)
committed
for l in "$copydir"/logdest/*; do
[[ $l == */logpipe.* ]] && continue
chown "$src_owner" "$l"
Jan Alexander Steffens (heftig)
committed
mv "$l" "$LOGDEST"
for s in "$copydir"/srcpkgdest/*; do
chown "$src_owner" "$s"
mv "$s" "$SRCPKGDEST"
done
Jan Alexander Steffens (heftig)
committed
}
# }}}
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
orig_argv=("$@")
while getopts 'hcur:I:l:nTD:d:' arg; do
case "$arg" in
c) clean_first=true ;;
D) bindmounts_ro+=(--bind-ro="$OPTARG") ;;
d) bindmounts_rw+=(--bind="$OPTARG") ;;
u) update_first=true ;;
r) passeddir="$OPTARG" ;;
I) install_pkgs+=("$OPTARG") ;;
l) copy="$OPTARG" ;;
n) run_namcap=true; makepkg_args+=(-i) ;;
T) temp_chroot=true; copy+="-$$" ;;
h|*) usage ;;
esac
done
[[ ! -f PKGBUILD && -z "${install_pkgs[*]}" ]] && die 'This must be run in a directory containing a PKGBUILD.'
check_root "$0" "${orig_argv[@]}"
# Canonicalize chrootdir, getting rid of trailing /
chrootdir=$(readlink -e "$passeddir")
[[ ! -d $chrootdir ]] && die "No chroot dir defined, or invalid path '%s'" "$passeddir"
[[ ! -d $chrootdir/root ]] && die "Missing chroot dir root directory. Try using: mkarchroot %s/root base-devel" "$chrootdir"
# Detect chrootdir filesystem type
chroottype=$(stat -f -c %T "$chrootdir")
if [[ ${copy:0:1} = / ]]; then
copydir=$copy
else
copydir="$chrootdir/$copy"
fi
# Pass all arguments after -- right to makepkg
makepkg_args+=("${@:$OPTIND}")
# See if -R was passed to makepkg
for arg in "${@:OPTIND}"; do
case ${arg%%=*} in
-*R*|--repackage)
repack=true
break 2
;;
esac
done
if [[ -n $SUDO_USER ]]; then
eval "USER_HOME=~$SUDO_USER"
else
USER_HOME=$HOME
fi
Jan Alexander Steffens (heftig)
committed
umask 0022
XDG_PACMAN_DIR="${XDG_CONFIG_HOME:-$USER_HOME/.config}/pacman"
if [[ -r "$XDG_PACMAN_DIR/makepkg.conf" ]]; then
load_vars "$XDG_PACMAN_DIR/makepkg.conf"
elif [[ -r "$USER_HOME/.makepkg.conf" ]]; then
load_vars "$USER_HOME/.makepkg.conf"
fi
Jan Alexander Steffens (heftig)
committed
load_vars /etc/makepkg.conf
# Use PKGBUILD directory if these don't exist
[[ -d $PKGDEST ]] || PKGDEST=$PWD
[[ -d $SRCDEST ]] || SRCDEST=$PWD
[[ -d $SRCPKGDEST ]] || SRCPKGDEST=$PWD
[[ -d $LOGDEST ]] || LOGDEST=$PWD
Jan Alexander Steffens (heftig)
committed
create_chroot
$update_first && arch-nspawn "$copydir" \
"${bindmounts_ro[@]}" "${bindmounts_rw[@]}" \
pacman -Syu --noconfirm
Jan Alexander Steffens (heftig)
committed
[[ -n ${install_pkgs[*]} ]] && install_packages
download_sources
prepare_chroot
Jan Alexander Steffens (heftig)
committed
if arch-nspawn "$copydir" \
--bind="$PWD:/startdir" \
--bind="$SRCDEST:/srcdest" \
"${bindmounts_ro[@]}" "${bindmounts_rw[@]}" \
Jan Alexander Steffens (heftig)
committed
/chrootbuild
then
move_products
Jan Alexander Steffens (heftig)
committed
(( ret += 1 ))
Jan Alexander Steffens (heftig)
committed
$temp_chroot && clean_temporary
Jan Alexander Steffens (heftig)
committed
if (( ret != 0 )); then
if $temp_chroot; then
die "Build failed"
die "Build failed, check %s/build" "$copydir"