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.
Jan Alexander Steffens (heftig)
committed
makepkg_args='-s --noconfirm -L --holdver'
repack=false
update_first=false
clean_first=false
install_pkg=
run_namcap=false
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. All unrecognized arguments passed to this script'
echo ' 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 '-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'
while getopts 'hcur:I:l:nT' arg; do
c) clean_first=true ;;
u) update_first=true ;;
Pierre Schmitz
committed
n) run_namcap=true; makepkg_args="$makepkg_args -i" ;;
Jan Alexander Steffens (heftig)
committed
T) temp_chroot=true; copy+="-$$" ;;
*) makepkg_args="$makepkg_args -$arg $OPTARG" ;;
Jan Alexander Steffens (heftig)
committed
(( EUID != 0 )) && die 'This script must be run as root.'
[[ ! -f PKGBUILD && -z "${install_pkgs[*]}" ]] && die 'This must be run in a directory containing a PKGBUILD.'
# Canonicalize chrootdir, getting rid of trailing /
Jan Alexander Steffens (heftig)
committed
[[ ! -d $chrootdir ]] && die "No chroot dir defined, or invalid path '$passeddir'"
[[ ! -d $chrootdir/root ]] && die "Missing chroot dir root directory. Try using: mkarchroot $chrootdir/root base-devel"
# 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="$makepkg_args ${*:$OPTIND}"
# See if -R was passed to makepkg
for arg in ${*:$OPTIND}; do
if [[ $arg = -R ]]; then
Jan Alexander Steffens (heftig)
committed
if [[ -n $SUDO_USER ]]; then
USER_HOME=$(eval echo ~$SUDO_USER)
else
USER_HOME=$HOME
fi
Jan Alexander Steffens (heftig)
committed
# {{{ functions
load_vars() {
local makepkg_conf="$1" var
Jan Alexander Steffens (heftig)
committed
[[ -f $makepkg_conf ]] || return 1
Jan Alexander Steffens (heftig)
committed
for var in {SRC,PKG,LOG}DEST MAKEFLAGS PACKAGER; do
[[ -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 ]]; then
if [[ -d $copydir ]]; then
btrfs subvolume delete "$copydir" >/dev/null ||
die "Unable to delete subvolume $copydir"
fi
btrfs subvolume snapshot "$chrootdir/root" "$copydir" >/dev/null ||
die "Unable to create subvolume $copydir"
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
}
Jan Alexander Steffens (heftig)
committed
clean_temporary() {
stat_busy "Removing temporary copy [$copy]"
if [[ "$chroottype" == btrfs ]]; then
Jan Alexander Steffens (heftig)
committed
btrfs subvolume delete "$copydir" >/dev/null ||
die "Unable to delete subvolume $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 $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 pkgname
for install_pkg in "${install_pkgs[@]}"; do
pkgname="${install_pkg##*/}"
cp "$install_pkg" "$copydir/$pkgname"
arch-nspawn "$copydir" pacman -U /$pkgname --noconfirm
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"
Jan Alexander Steffens (heftig)
committed
mkdir -p "$copydir/build"
if ! grep -q 'BUILDDIR="/build"' "$copydir/etc/makepkg.conf"; then
echo 'BUILDDIR="/build"' >> "$copydir/etc/makepkg.conf"
fi
Jan Alexander Steffens (heftig)
committed
# Read .makepkg.conf and .gnupg/pubring.gpg even if called via sudo
if [[ -r "$USER_HOME/.gnupg/pubring.gpg" ]]; then
install -D "$USER_HOME/.gnupg/pubring.gpg" \
"$copydir/build/.gnupg/pubring.gpg"
fi
Jan Alexander Steffens (heftig)
committed
mkdir -p "$copydir/pkgdest"
if ! grep -q 'PKGDEST="/pkgdest"' "$copydir/etc/makepkg.conf"; then
echo 'PKGDEST="/pkgdest"' >> "$copydir/etc/makepkg.conf"
fi
Jan Alexander Steffens (heftig)
committed
mkdir -p "$copydir/logdest"
if ! grep -q 'LOGDEST="/logdest"' "$copydir/etc/makepkg.conf"; then
echo 'LOGDEST="/logdest"' >> "$copydir/etc/makepkg.conf"
fi
Jan Alexander Steffens (heftig)
committed
# These two get bind-mounted
mkdir -p "$copydir/startdir" "$copydir/startdir_host"
mkdir -p "$copydir/srcdest" "$copydir/srcdest_host"
if ! grep -q 'SRCDEST="/srcdest"' "$copydir/etc/makepkg.conf"; then
echo 'SRCDEST="/srcdest"' >> "$copydir/etc/makepkg.conf"
fi
Jan Alexander Steffens (heftig)
committed
chown -R nobody "$copydir"/{build,pkgdest,logdest,srcdest,startdir}
Jan Alexander Steffens (heftig)
committed
if [[ -n $MAKEFLAGS ]]; then
sed -i '/^MAKEFLAGS=/d' "$copydir/etc/makepkg.conf"
echo "MAKEFLAGS='${MAKEFLAGS}'" >> "$copydir/etc/makepkg.conf"
fi
Jan Alexander Steffens (heftig)
committed
if [[ -n $PACKAGER ]]; then
sed -i '/^PACKAGER=/d' "$copydir/etc/makepkg.conf"
echo "PACKAGER='${PACKAGER}'" >> "$copydir/etc/makepkg.conf"
fi
Jan Alexander Steffens (heftig)
committed
if [[ ! -f $copydir/etc/sudoers.d/nobody-pacman ]]; then
cat > "$copydir/etc/sudoers.d/nobody-pacman" <<EOF
Defaults env_keep += "HOME"
nobody ALL = NOPASSWD: /usr/bin/pacman
EOF
Jan Alexander Steffens (heftig)
committed
chmod 440 "$copydir/etc/sudoers.d/nobody-pacman"
fi
Jan Alexander Steffens (heftig)
committed
# This is a little gross, but this way the script is recreated every time in the
# working copy
cat >"$copydir/chrootbuild" <<EOF
#!/bin/bash
Jan Alexander Steffens (heftig)
committed
shopt -s nullglob
Jan Alexander Steffens (heftig)
committed
# Workaround makepkg disliking read-only dirs
ln -sft /srcdest /srcdest_host/*
ln -sft /startdir /startdir_host/*
# Keep PKGBUILD writable for pkgver()
rm /startdir/PKGBUILD*
cp /startdir_host/PKGBUILD* /startdir
chown nobody /startdir/PKGBUILD*
Jan Alexander Steffens (heftig)
committed
cd /startdir
sudo -u nobody makepkg $makepkg_args || exit 1
if $run_namcap; then
pacman -S --needed --noconfirm namcap
Jan Alexander Steffens (heftig)
committed
for pkgfile in /startdir/PKGBUILD /pkgdest/*; do
echo "Checking \${pkgfile##*/}"
Jan Alexander Steffens (heftig)
committed
sudo -u nobody namcap "\$pkgfile" 2>&1 | tee "/logdest/\${pkgfile##*/}-namcap.log"
Jan Alexander Steffens (heftig)
committed
chmod +x "$copydir/chrootbuild"
}
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
chown "$src_owner" "$l"
Jan Alexander Steffens (heftig)
committed
mv "$l" "$LOGDEST"
Jan Alexander Steffens (heftig)
committed
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
}
# }}}
umask 0022
load_vars "$USER_HOME/.makepkg.conf"
load_vars /etc/makepkg.conf
# Use PKGBUILD directory if these don't exist
[[ -d $PKGDEST ]] || PKGDEST=$PWD
[[ -d $SRCDEST ]] || SRCDEST=$PWD
[[ -d $LOGDEST ]] || LOGDEST=$PWD
create_chroot
$update_first && arch-nspawn "$copydir" pacman -Syu --noconfirm
[[ -n ${install_pkgs[*]} ]] && install_packages
prepare_chroot
download_sources
if arch-nspawn "$copydir" \
--bind-ro="$PWD:/startdir_host" \
--bind-ro="$SRCDEST:/srcdest_host" \
/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"
Jan Alexander Steffens (heftig)
committed
die "Build failed, check $copydir/build"