summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Alexander Steffens (heftig) <jan.steffens@gmail.com>2013-05-02 08:29:21 +0200
committerJan Alexander Steffens (heftig) <jan.steffens@gmail.com>2013-05-03 08:48:14 +0200
commit7ca4eb82ddb791881dc5c666f0d2400f3904c152 (patch)
tree106453871a4c4027140fea511172320f9888813f
parentabba9f07a6d703cd97fc2d2bbd397072d5bf796d (diff)
makechrootpkg: Avoid parsing PKGBUILD and support VCS sources
- Ensure sources are available before entering chroot - Bind STARTDIR and SRCDEST into the chroot read-only - Refactor makechrootpkg and introduce meaningful functions Avoids copying stuff from/to the chroot as much as possible. With VCS sources these copies can get quite expensive.
-rw-r--r--makechrootpkg.in321
1 files changed, 173 insertions, 148 deletions
diff --git a/makechrootpkg.in b/makechrootpkg.in
index fd1d432..0d93c2e 100644
--- a/makechrootpkg.in
+++ b/makechrootpkg.in
@@ -12,7 +12,7 @@ m4_include(lib/common.sh)
shopt -s nullglob
-makepkg_args='-s --noconfirm -L'
+makepkg_args='-s --noconfirm -L --holdver'
repack=false
update_first=false
clean_first=false
@@ -70,13 +70,22 @@ while getopts 'hcur:I:l:nT' arg; do
I) install_pkgs+=("$OPTARG") ;;
l) copy="$OPTARG" ;;
n) run_namcap=true; makepkg_args="$makepkg_args -i" ;;
- T) temp_chroot=true; copy+="-$RANDOM" ;;
+ T) temp_chroot=true; copy+="-$$" ;;
*) makepkg_args="$makepkg_args -$arg $OPTARG" ;;
esac
done
+(( 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 /
chrootdir=$(readlink -e "$passeddir")
+[[ ! -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
@@ -95,54 +104,72 @@ for arg in ${*:$OPTIND}; do
fi
done
-if (( EUID )); then
- die 'This script must be run as root.'
-fi
-
-if [[ ! -f PKGBUILD && -z "${install_pkgs[*]}" ]]; then
- die 'This must be run in a directory containing a PKGBUILD.'
+if [[ -n $SUDO_USER ]]; then
+ USER_HOME=$(eval echo ~$SUDO_USER)
+else
+ USER_HOME=$HOME
fi
-if [[ ! -d $chrootdir ]]; then
- die "No chroot dir defined, or invalid path '$passeddir'"
-fi
+# {{{ functions
+load_vars() {
+ local makepkg_conf="$1" var
-if [[ ! -d $chrootdir/root ]]; then
- die "Missing chroot dir root directory. Try using: mkarchroot $chrootdir/root base-devel"
-fi
+ [[ -f $makepkg_conf ]] || return 1
-umask 0022
+ for var in {SRC,PKG,LOG}DEST MAKEFLAGS PACKAGER; do
+ [[ -z ${!var} ]] && eval $(grep "^${var}=" "$makepkg_conf")
+ done
-# Detect chrootdir filesystem type
-chroottype=$(stat -f -c %T "$chrootdir")
+ return 0
+}
-# Lock the chroot we want to use. We'll keep this lock until we exit.
-lock 9 "$copydir.lock" "Locking chroot copy [$copy]"
+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
-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"
+ # Drop the read lock again
+ exec 8>&-
+ fi
+}
- stat_busy "Creating clean working copy [$copy]"
+clean_temporary() {
+ stat_busy "Removing temporary 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"
+ btrfs subvolume delete "$copydir" >/dev/null ||
+ die "Unable to delete subvolume $copydir"
else
- mkdir -p "$copydir"
- rsync -a --delete -q -W -x "$chrootdir/root/" "$copydir"
+ # avoid change of filesystem in case of an umount failure
+ rm --recursive --force --one-file-system "$copydir" ||
+ die "Unable to delete $copydir"
fi
+
+ # remove lock file
+ rm -f "$copydir.lock"
stat_done
+}
- # Drop the read lock again
- exec 8>&-
-fi
+install_packages() {
+ local pkgname
-if [[ -n "${install_pkgs[*]}" ]]; then
for install_pkg in "${install_pkgs[@]}"; do
pkgname="${install_pkg##*/}"
cp "$install_pkg" "$copydir/$pkgname"
@@ -153,161 +180,159 @@ if [[ -n "${install_pkgs[*]}" ]]; then
rm "$copydir/$pkgname"
done
- # If there is no PKGBUILD we have done
+ # If there is no PKGBUILD we are done
[[ -f PKGBUILD ]] || exit $ret
-fi
-
-$update_first && arch-nspawn "$copydir" pacman -Syu --noconfirm
-
-mkdir -p "$copydir/build"
+}
-# Remove anything in there UNLESS -R (repack) was passed to makepkg
-$repack || rm -rf "$copydir"/build/*
+prepare_chroot() {
+ $repack || rm -rf "$copydir/build"
-# Read .makepkg.conf and .gnupg/pubring.gpg even if called via sudo
-if [[ -n $SUDO_USER ]]; then
- SUDO_HOME="$(eval echo ~$SUDO_USER)"
- makepkg_conf="$SUDO_HOME/.makepkg.conf"
- if [[ -r "$SUDO_HOME/.gnupg/pubring.gpg" ]]; then
- install -D "$SUDO_HOME/.gnupg/pubring.gpg" "$copydir/build/.gnupg/pubring.gpg"
+ mkdir -p "$copydir/build"
+ if ! grep -q 'BUILDDIR="/build"' "$copydir/etc/makepkg.conf"; then
+ echo 'BUILDDIR="/build"' >> "$copydir/etc/makepkg.conf"
fi
-else
- makepkg_conf="$HOME/.makepkg.conf"
- if [[ -r "$HOME/.gnupg/pubring.gpg" ]]; then
- install -D "$HOME/.gnupg/pubring.gpg" "$copydir/build/.gnupg/pubring.gpg"
- fi
-fi
-# Get SRC/PKGDEST from makepkg.conf
-if [[ -f $makepkg_conf ]]; then
- eval $(grep '^SRCDEST=' "$makepkg_conf")
- eval $(grep '^PKGDEST=' "$makepkg_conf")
- eval $(grep '^MAKEFLAGS=' "$makepkg_conf")
- eval $(grep '^PACKAGER=' "$makepkg_conf")
-fi
-
-[[ -z $SRCDEST ]] && eval $(grep '^SRCDEST=' /etc/makepkg.conf)
-[[ -z $PKGDEST ]] && eval $(grep '^PKGDEST=' /etc/makepkg.conf)
-[[ -z $MAKEFLAGS ]] && eval $(grep '^MAKEFLAGS=' /etc/makepkg.conf)
-[[ -z $PACKAGER ]] && eval $(grep '^PACKAGER=' /etc/makepkg.conf)
-
-# Use PKGBUILD directory if PKGDEST or SRCDEST don't exist
-[[ -d $PKGDEST ]] || PKGDEST=.
-[[ -d $SRCDEST ]] || SRCDEST=.
-
-mkdir -p "$copydir/pkgdest"
-if ! grep -q 'PKGDEST="/pkgdest"' "$copydir/etc/makepkg.conf"; then
- echo 'PKGDEST="/pkgdest"' >> "$copydir/etc/makepkg.conf"
-fi
+ # 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
-mkdir -p "$copydir/srcdest"
-if ! grep -q 'SRCDEST="/srcdest"' "$copydir/etc/makepkg.conf"; then
- echo 'SRCDEST="/srcdest"' >> "$copydir/etc/makepkg.conf"
-fi
+ mkdir -p "$copydir/pkgdest"
+ if ! grep -q 'PKGDEST="/pkgdest"' "$copydir/etc/makepkg.conf"; then
+ echo 'PKGDEST="/pkgdest"' >> "$copydir/etc/makepkg.conf"
+ fi
-if [[ -n $MAKEFLAGS ]]; then
- sed -i '/^MAKEFLAGS=/d' "$copydir/etc/makepkg.conf"
- echo "MAKEFLAGS='${MAKEFLAGS}'" >> "$copydir/etc/makepkg.conf"
-fi
+ mkdir -p "$copydir/logdest"
+ if ! grep -q 'LOGDEST="/logdest"' "$copydir/etc/makepkg.conf"; then
+ echo 'LOGDEST="/logdest"' >> "$copydir/etc/makepkg.conf"
+ fi
-if [[ -n $PACKAGER ]]; then
- sed -i '/^PACKAGER=/d' "$copydir/etc/makepkg.conf"
- echo "PACKAGER='${PACKAGER}'" >> "$copydir/etc/makepkg.conf"
-fi
+ # 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
-# Set target CARCH as it might be used within the PKGBUILD to select correct sources
-eval $(grep '^CARCH=' "$copydir/etc/makepkg.conf")
-export CARCH
-
-# Copy PKGBUILD and sources
-cp PKGBUILD "$copydir/build/"
-(
- source PKGBUILD
- for file in "${source[@]}"; do
- file="${file%%::*}"
- file="${file##*://*/}"
- if [[ -f $file ]]; then
- cp "$file" "$copydir/srcdest/"
- elif [[ -f $SRCDEST/$file ]]; then
- cp "$SRCDEST/$file" "$copydir/srcdest/"
- fi
- done
+ chown -R nobody "$copydir"/{build,pkgdest,logdest,srcdest,startdir}
- # Find all changelog and install files, even inside functions
- for i in 'changelog' 'install'; do
- while read -r file; do
- # evaluate any bash variables used
- eval file=\"$(sed 's/^\(['\''"]\)\(.*\)\1$/\2/' <<< "$file")\"
- [[ -f $file ]] && cp "$file" "$copydir/build/"
- done < <(sed -n "s/^[[:space:]]*$i=//p" PKGBUILD)
- done
-)
+ if [[ -n $MAKEFLAGS ]]; then
+ sed -i '/^MAKEFLAGS=/d' "$copydir/etc/makepkg.conf"
+ echo "MAKEFLAGS='${MAKEFLAGS}'" >> "$copydir/etc/makepkg.conf"
+ fi
-chown -R nobody "$copydir"/{build,pkgdest,srcdest}
+ if [[ -n $PACKAGER ]]; then
+ sed -i '/^PACKAGER=/d' "$copydir/etc/makepkg.conf"
+ echo "PACKAGER='${PACKAGER}'" >> "$copydir/etc/makepkg.conf"
+ fi
-cat > "$copydir/etc/sudoers.d/nobody-pacman" <<EOF
+ 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
-chmod 440 "$copydir/etc/sudoers.d/nobody-pacman"
+ chmod 440 "$copydir/etc/sudoers.d/nobody-pacman"
+ fi
-# This is a little gross, but this way the script is recreated every time in the
-# working copy
-cat >"$copydir/chrootbuild" <<EOF
+ # This is a little gross, but this way the script is recreated every time in the
+ # working copy
+ cat >"$copydir/chrootbuild" <<EOF
#!/bin/bash
. /etc/profile
export HOME=/build
+shopt -s nullglob
-cd /build
+# Workaround makepkg disliking read-only dirs
+ln -sft /srcdest /srcdest_host/*
+ln -sft /startdir /startdir_host/*
+
+cd /startdir
sudo -u nobody makepkg $makepkg_args || exit 1
if $run_namcap; then
pacman -S --needed --noconfirm namcap
- for pkgfile in /build/PKGBUILD /pkgdest/*.pkg.tar.?z; do
+ for pkgfile in /startdir/PKGBUILD /pkgdest/*; do
echo "Checking \${pkgfile##*/}"
- sudo -u nobody namcap "\$pkgfile" 2>&1 | tee "/build/\${pkgfile##*/}-namcap.log"
+ sudo -u nobody namcap "\$pkgfile" 2>&1 | tee "/logdest/\${pkgfile##*/}-namcap.log"
done
fi
exit 0
EOF
-chmod +x "$copydir/chrootbuild"
+ 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
+}
-if arch-nspawn "$copydir" /chrootbuild; then
- for pkgfile in "$copydir"/pkgdest/*.pkg.tar.?z; do
+move_products() {
+ for pkgfile in "$copydir"/pkgdest/*; do
chown "$src_owner" "$pkgfile"
mv "$pkgfile" "$PKGDEST"
done
- for l in "$copydir"/build/*-{build,check,namcap,package,package_*}.log; do
+ for l in "$copydir"/logdest/*; do
chown "$src_owner" "$l"
- [[ -f $l ]] && mv "$l" .
+ mv "$l" "$LOGDEST"
done
+}
+# }}}
+
+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
else
- # Just in case. We returned 1, make sure we fail
- ret=1
+ (( ret += 1 ))
fi
-for f in "$copydir"/srcdest/*; do
- chown "$src_owner" "$f"
- mv "$f" "$SRCDEST"
-done
+$temp_chroot && clean_temporary
-if $temp_chroot; then
- stat_busy "Removing temporary directoy [$copy]"
- if [[ "$chroottype" == btrfs ]]; then
- btrfs subvolume delete "$copydir" >/dev/null ||
- die "Unable to delete subvolume $copydir"
+if (( ret != 0 )); then
+ if $temp_chroot; then
+ die "Build failed"
else
- # avoid change of filesystem in case of an umount failure
- rm --recursive --force --one-file-system "$copydir" ||
- die "Unable to delete $copydir"
+ die "Build failed, check $copydir/build"
fi
- # remove lock file
- rm --force "$copydir.lock"
- stat_done
-elif (( ret != 0 )); then
- die "Build failed, check $copydir/build"
else
true
fi