diff options
Diffstat (limited to 'src/devtools/makechrootpkg.in')
-rw-r--r-- | src/devtools/makechrootpkg.in | 328 |
1 files changed, 328 insertions, 0 deletions
diff --git a/src/devtools/makechrootpkg.in b/src/devtools/makechrootpkg.in new file mode 100644 index 0000000..9e5b04f --- /dev/null +++ b/src/devtools/makechrootpkg.in @@ -0,0 +1,328 @@ +#!/bin/bash +# Copyright 2011-2012 The Arch Linux Development Team +# Copyright 2012 Luke Shumaker +# +# 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. + +# Because of how we pull changes from devtools.git, some of the function +# bodies are not indented. I appologize for my sins against the reader. + +# Any function beginning with "_makechrootpkg_" is basically a multiline +# comment to minimize the diff. These functions may use different variable +# names. + +# Otherwise, a function may be affected by the variables: +# makepkg.conf +# - PKGDEST +# - SRCDEST +# chroot.conf (and derived) +# - CHROOT +# - CHROOTCOPY +# - rootdir # = $CHROOTDIR/$CHROOT/root +# - copydir # = $CHROOTDIR/$CHROOT/$CHROOTCOPY +# environment +# - LIBREUSER # conf.sh +# - LIBREHOME # conf.sh +# - INCHROOT # libremakepkg +# invocation +# - repack + +[[ -n ${repack:-} ]] || repack=false + +_makechrootpkg_init() { +m4_include(lib/common.sh) + +shopt -s nullglob + +makepkg_args='-s --noconfirm -L' +repack=false +update_first=false +clean_first=false +install_pkg= +add_to_db=false +run_namcap=false +chrootdir= +passeddir= + +default_copy=$USER +[[ -n $SUDO_USER ]] && default_copy=$SUDO_USER +[[ -z $default_copy || $default_copy = root ]] && default_copy=copy +src_owner=${SUDO_USER:-$USER} +} + +_makechrootpkg_usage() { + 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 base-devel sudo' + echo '' + 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 '-d Add the package to a local db at /repo after building' + 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: $default_copy" + echo '-n Run namcap on the package' + exit 1 +} + +_makechrootpkg_parse_options_init() { +while getopts 'hcudr:I:l:n' arg; do + case "$arg" in + h) usage ;; + c) clean_first=true ;; + u) update_first=true ;; + d) add_to_db=true ;; + r) passeddir="$OPTARG" ;; + I) install_pkg="$OPTARG" ;; + l) copy="$OPTARG" ;; + n) run_namcap=true; makepkg_args="$makepkg_args -i" ;; + *) makepkg_args="$makepkg_args -$arg $OPTARG" ;; + esac +done + +# Canonicalize chrootdir, getting rid of trailing / +chrootdir=$(readlink -e "$passeddir") + +if [[ ${copy:0:1} = / ]]; then + copydir=$copy +else + [[ -z $copy ]] && copy=$default_copy + 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 + repack=true + break + fi +done + +if (( EUID )); then + die 'This script must be run as root.' +fi + +if [[ ! -f PKGBUILD && -z $install_pkg ]]; then + die 'This must be run in a directory containing a PKGBUILD.' +fi + +if [[ ! -d $chrootdir ]]; then + die "No chroot dir defined, or invalid path '$passeddir'" +fi + +if [[ ! -d $chrootdir/root ]]; then + die "Missing chroot dir root directory. Try using: mkarchroot $chrootdir/root base base-devel sudo" +fi + +umask 0022 + +# Lock the chroot we want to use. We'll keep this lock until we exit. +# Note this is the same FD number as in mkarchroot +lock_open_write 9 "$copydir.lock" "Locking chroot copy '$copy'" +} + +chroot_sync() { + if [[ $CHROOTCOPY = root ]]; then + error "Cannot sync the root copy with itself" + exit 1 + fi + # Get a read lock on the root chroot to make + # sure we don't clone a half-updated chroot + lock_open_read 8 "$rootdir" \ + "Waiting for existing lock on \`$rootdir' to be released" + + stat_busy 'Creating clean working copy' + local use_rsync=false + if type -P btrfs >/dev/null; then + [[ -d $copydir ]] && btrfs subvolume delete "$copydir" &>/dev/null + btrfs subvolume snapshot "$rootdir" "$copydir" &>/dev/null || + use_rsync=true + else + use_rsync=true + fi + + if $use_rsync; then + mkdir -p "$copydir" + rsync -a --delete -q -W -x "$rootdir/" "$copydir" + fi + stat_done + + # Drop the read lock again + lock_close 8 +} + +_makechrootpkg_install_pkg() { + pkgname="${install_pkg##*/}" + cp "$install_pkg" "$copydir/$pkgname" + + mkarchroot -r "pacman -U /$pkgname --noconfirm" "$copydir" + ret=$? + + rm "$copydir/$pkgname" + + # Exit early, we've done all we need to + exit $ret +} + +chroot_init() { +# make sure the chroot exists +librechroot -n "$CHROOT" -l "$CHROOTCOPY" -m + +mkdir -p "$copydir/build" +mkdir -p "$copydir/pkgdest" +mkdir -p "$copydir/srcdest" + +# Remove anything in there UNLESS -R (repack) was passed to makepkg +$repack || rm -rf "$copydir"/build/* + +if [[ -r "$LIBREHOME/.gnupg/pubring.gpg" ]]; then + install -D "$LIBREHOME/.gnupg/pubring.gpg" "$copydir/build/.gnupg/pubring.gpg" +fi +rm -f "$copydir/build/.makepkg.conf" + +MAKEPKG_CONF="$copydir/etc/makepkg.conf" set_conf_makepkg PKGDEST /pkgdest +MAKEPKG_CONF="$copydir/etc/makepkg.conf" set_conf_makepkg SRCDEST /srcdest + +if grep -q '^\[repo\]' "$copydir/etc/pacman.conf"; then + cat >> "$copydir/etc/makepkg.conf" <<EOF +[repo] +SigLevel = Optional TrustAll +Server = file:///repo +EOF +fi + +_let_nobody_use_pacman +} + +chroot_copy_in() { +# Copy PKGBUILD and sources +cp PKGBUILD "$copydir/build/" +( + set +euE + source PKGBUILD + # Copy source files + 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 + + # 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 +) + +chown -R nobody "$copydir"/{build,pkgdest,srcdest} +} + +_let_nobody_use_pacman() { +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" +} + +chroot_exec() { +local HASNET=true +[[ $1 == -N ]] && { HASNET=false; shift; } + +local cmd="$*" +# This is a little gross, but this way the script is recreated every time in the +# working copy +cat >"$copydir/chrootexec" <<EOF +#!/bin/bash +. /etc/profile +${INCHROOT} || export HOME=/build +${INCHROOT} || cd /build + +${cmd} +EOF +chmod 755 "$copydir/chrootexec" + +local flags='' +if $INCHROOT; then + $HASNET || flags='-n' + unshare $flags -- /chrootexec +else + $HASNET || flags='-N' + librechroot $flags -n "$CHROOT" -l "$CHROOTCOPY" -r /chrootexec +fi +} + +add_to_local_repo() { + for pkgfile in "$copydir"/pkgdest/*.pkg.tar*; do + if true; then + mkdir -p "$copydir/repo" + pushd "$copydir/repo" >/dev/null + cp "$pkgfile" . + repo-add repo.db.tar.gz "${pkgfile##*/}" + popd >/dev/null + fi + done +} +_chroot_copy_out_pkgs() { + for pkgfile in "$copydir"/pkgdest/*.pkg.tar*; do + chown "$LIBREUSER" "$pkgfile" + mv "$pkgfile" "$PKGDEST" + if [[ $PKGDEST != . ]]; then + ln -s "$PKGDEST/${pkgfile##*/}" . + fi + done +} + +_chroot_copy_out_logs() { + for l in "$copydir"/build/*.log; do + chown "$LIBREUSER" "$l" + [[ -f $l ]] && mv "$l" . + done +} + +_chroot_copy_out_srcs() { +for f in "$copydir"/srcdest/*; do + chown "$LIBREUSER" "$f" + mv "$f" "$SRCDEST" +done +} + +chroot_copy_out() { + _chroot_copy_out_pkgs + _chroot_copy_out_logs + _chroot_copy_out_srcs +} |