#!/usr/bin/env bash set -euE # libremakepkg # Copyright (C) 2010-2012 Nicolás Reynolds # Copyright (C) 2010-2012 Joshua Ismael Haase Hernández (xihh) # Copyright (C) 2012 Michał Masłowski # Copyright (C) 2012-2015, 2017-2018 Luke Shumaker # # License: GNU GPLv2+ # # 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 2 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 . source "$(librelib conf)" source "$(librelib messages)" source "$(librelib chroot/makechrootpkg)" set -o pipefail shopt -s nullglob umask 0022 # Global variables: readonly _indent="$(librelib chroot/indent)" readonly INCHROOT=$([[ -f /.arch-chroot ]] && echo true || echo false) SANE=true # can be changed with the -I flag 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 set by conf.sh librechroot_flags=() # Hooks ######################################################################## hook_pre_build=(:) hook_post_build=(:) hook_check_pkgbuild=(:) hook_check_pkg=(:) source "$(librelib chroot/hooks-chcleanup.sh)" source "$(librelib chroot/hooks-check.sh)" source "$(librelib chroot/hooks-distcc.sh)" # Boring/mundane functions ##################################################### indent() { "$_indent" ' | ' } # 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 # The "& wait $!" trick prevents "||" from disabling "set -e" { "$hook" "$@" |& indent; } & wait $! || fails+=("$hook") done if [[ ${#fails[@]} -gt 0 ]]; then error "Failure(s) in %s: %s" "$hookname" "${fails[*]}" return $EXIT_FAILURE else return $EXIT_SUCCESS 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+=('chroot_cleanup') chroot_cleanup() { local copydir=$1 rm -f -- "$copydir"/chrootbuild } build() ( DBG "libremakepkg::build() IN" local copydir=$1 local srcpkg=$2 local repack=$3 local makepkg_args=("${@:4}") local startdir startdir=$(mktemp -d) chown "$LIBREUSER:" "$startdir" trap "rm -rf -- ${startdir@Q}" EXIT sudo -u "$LIBREUSER" bsdtar -xf "$srcpkg" -C "$startdir" --strip-components 1 local run_ynet=() local run_nnet=() if $INCHROOT; then local _run=(sh -c "mount --bind -o ro -- ${startdir@Q} ${startdir@Q} && cd ${startdir@Q} && \$@" --) run_ynet=(unshare --mount -- "${_run[@]}") run_nnet=(unshare --mount --net -- "${_run[@]}") else librechroot_flags+=(-r "$startdir:/startdir") run_ynet=(librechroot "${librechroot_flags[@]}" run) run_nnet=(librechroot "${librechroot_flags[@]}" -N run) fi $NONET || run_nnet=("${run_ynet[@]}") DBG "libremakepkg::build() prepare_chroot" prepare_chroot "$copydir" "$LIBREHOME" "$repack" false # generates 'chrootbuild' DBG "libremakepkg::build() pre_build run_nnet=${run_nnet[*]}" run_hook pre_build "$copydir" trap "run_hook post_build ${copydir@Q}; rm -rf -- ${startdir@Q}" EXIT "${run_nnet[@]}" /chrootbuild "${makepkg_args[@]}" < /dev/null |& indent DBG "libremakepkg::build() OUT" ) # The main program ############################################################# usage() { local mount_msg="$(_ 'PATH[:INSIDE_PATH[:OPTIONS]]')" print "Usage: %s [options]" "${0##*/}" print 'This program will build your package.' echo prose 'If run from outside of a chroot, this 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 `MAKEFLAGS` and `PACKAGER` in `/etc/makepkg.conf` to reflect the value outside of the chroot' bullet 'create a `builduser` with the same numeric UID as $LIBREUSER' bullet 'let this `builduser` call `sudo pacman` without a password' bullet 'set `keyserver-options` in `~builduser/.gnupg/gpg.conf`' bullet 'copy the GPG pubring of the host $LIBREUSER, as the GPG pubring of the chroot `builduser`' bullet 'add a pacman repositoriy of locally built packages' prose '$LIBREUSER is determined at runtime, per /usr/lib/libretools/conf.sh. $LIBREUSER is normally the login of the invoking user, per $SUDO_USER; but may be over-ridden by setting $LIBREUSER in the `configuration` section of conf.sh.' echo prose 'This command is configured both with `chroot.conf` (either in `/etc/libretools.d/` or `$XDG_CONFIG_HOME/libretools/`), and with makepkg.conf(5). However, similarly to makepkg(8), it lets environment variables for {SRC,SRCPKG,PKG,LOG}DEST, MAKEFLAGS and PACKAGER override the settings in makepkg.conf(5).' echo prose 'The `-n` and `-l` options are passed to `librechroot` (see the librechroot documentation).' echo flag 'Options (librechroot):' \ "-n <$(_ NAME)>" 'Name of the chroot to use' \ "-l <$(_ COPY)>" 'Name of, or absolute path to, the chroot copy to use' \ '-I' "Allow packages older than their database entries to remain installed, after updating the database manually. This option can lead to an insane system configuaration. Replacement packages will still be applied, for example, unless they are helpd back explcitly; and there may be dependency conflicts or shared library mis-matches. Use this only in extreme circumstances; such as if you need to enable [testing] without synchronizing afterward." \ "-w <${mount_msg}>" 'Bind mount a file or directory, read/write' \ "-r <${mount_msg}>" 'Bind mount a file or directory, read-only' flag 'Options (libremakepkg):' \ '-N' "Don't disable networking during prepare(), build(), and package(). PLEASE don't use this unless you have a special reason, its use is a violation of Parabola policy." \ '-R' 'Repackage contents of the package without rebuilding' \ "-S <$(_ FILE)>" 'Use an existing --allsource source-package (not-yet-implemented)' \ '-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" exit $EXIT_INVALIDARGUMENT } main() { # Initial variable values ############################################## local copy=$([[ $LIBREUSER == root ]] && echo copy || echo "$LIBREUSER") local makepkg_args=(--syncdeps --noconfirm --log --holdver --skipinteg) local repack=false local chroot='' local srcpkg='' # Parse command line options ########################################### while getopts 'n:l:w:r:INRS:h' flag ; do case "${flag}" in n ) chroot=$OPTARG ; ! $INCHROOT || err_chflag "$flag";; l ) copy=$OPTARG ; ! $INCHROOT || err_chflag "$flag";; w|r) librechroot_flags+=(-$flag "$OPTARG") ; ! $INCHROOT || err_chflag "$flag";; I ) SANE=false;; N ) NONET=false;; R ) repack=true; makepkg_args+=(-R);; S ) srcpkg='';; # srcpkg=$OPTARG;; TODO: not yet implemented h ) usage; exit $EXIT_SUCCESS;; * ) usage >&2; exit $EXIT_INVALIDARGUMENT;; esac done shift $((OPTIND - 1)) if [[ $# != 0 ]]; then error 'Extra arguments: %s' "$*" exit $EXIT_INVALIDARGUMENT fi # Resolve the chroot path ############################################## local copydir if $INCHROOT; then copydir='/' else load_conf chroot.conf CHROOTDIR CHROOT || exit [[ -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 $EXIT_NOPERMISSION fi if [[ -n $srcpkg ]]; then if [[ ! -f $srcpkg ]]; then error 'Source package does not exist: %s' "$srcpkg" exit $EXIT_INVALIDARGUMENT fi else if [[ ! -f PKGBUILD ]]; then error "No PKGBUILD exists in the present directory." exit $EXIT_FAILURE fi fi # Make sure that the various *DEST directories exist sudo -u "$LIBREUSER" mkdir -p -- "$PKGDEST" "$SRCDEST" "$SRCPKGDEST" "$LOGDEST" # 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+=( -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 DBG "libremakepkg::main() GUARD whoami=$(whoami) id=$(id)" ; return 1 # Set target CARCH # note that we waited until after locking/creating the chroot to do this CARCH="$(MAKEPKG_CONF=$copydir/etc/makepkg.conf get_var makepkg CARCH)" export CARCH # Pre-build msg 'Starting pre-build activities...' run_hook check_pkgbuild if [[ -n $srcpkg ]]; then msg 'Using existing source package %s' "$srcpkg" # TODO: symlink $srcpkg to ${SRCPKGDEST}/${pkgbase}-${evr}-${CARCH}${SRCEXT} else local srcpkgdest="$(mktemp -d)" chown "$LIBREUSER:" "$srcpkgdest" # trap "rm -rf -- ${srcpkgdest@Q}" EXIT trap "echo TRAP ${srcpkgdest@Q} ; ls ${srcpkgdest@Q} ; rm -rf -- ${srcpkgdest@Q}" EXIT DBG "libremakepkg::main() ->download_sources IN srcpkgdest='$srcpkgdest'" msg 'Downloading sources...' SRCPKGDEST="$srcpkgdest" download_sources "$copydir" "$LIBREUSER" |& indent srcpkg=("$srcpkgdest"/*) DBG "libremakepkg::main() ->download_sources OUT srcpkg=${srcpkg[@]}" ; ls "$srcpkgdest" ; if (( ${#srcpkg[@]} != 1 )); then error 'Something went funny with makepkg --allsource' return $EXIT_FAILURE fi # We want to inject "-$pkgarch" in to srcpkg's filename, right before $SRCEXT local srcext pkgarch srcpkg_filename srcext="$(MAKEPKG_CONF=$copydir/etc/makepkg.conf get_var makepkg SRCEXT)" if [[ "$(bsdtar xfO "$srcpkg" --include='*/.SRCINFO' | grep $'\tarch =')" = $'\tarch = any' ]]; then pkgarch=any else pkgarch=$CARCH fi srcpkg_filename=${srcpkg##*/} srcpkg_filename=${srcpkg_filename%"${srcext}"}-${pkgarch}${srcext} mv -T -- "$srcpkg" "$SRCPKGDEST/${srcpkg_filename}" srcpkg="$SRCPKGDEST/${srcpkg_filename}" rmdir -- "${srcpkgdest}" trap EXIT fi DBG "libremakepkg::main() build \"$copydir\" \"$srcpkg\" \"$repack\" \"${makepkg_args[@]}\"" # Build msg 'Starting to build the package...' trap "exit_copy '$copydir' '$LIBREUSER'" EXIT build "$copydir" "$srcpkg" "$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 "$@"