diff options
Diffstat (limited to 'src/dagpkg')
-rwxr-xr-x | src/dagpkg | 204 |
1 files changed, 204 insertions, 0 deletions
diff --git a/src/dagpkg b/src/dagpkg new file mode 100755 index 0000000..49d6ef1 --- /dev/null +++ b/src/dagpkg @@ -0,0 +1,204 @@ +#!/usr/bin/env bash +# +# dagpkg - create a directed graph of package dependencies and build +# them in topological order +# +# (c) 2014 Nicolás Reynolds <fauno@parabola.nu> +# Michał Masłowski <mtjm@mtjm.eu> +# +# 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, either version 3 of the License, or +# (at your option) any later version. +# +# 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +set -e + +# Source variables from libretools +source /etc/libretools.conf +source $XDG_CONFIG_HOME/libretools/libretools.conf &>/dev/null || true + +# Source variables from makepkg +source /etc/makepkg.conf +source $XDG_CONFIG_HOME/.makepkg.conf &>/dev/null || true + +# End inmediately but print an useful message +trap_exit() { + term_title "error!" + error "(%s) %s (leftovers on %s)" \ + "${0##*/}" "$@" "${temp_dir}" + exit 1 +} + +# Trap signals from makepkg +trap 'trap_exit "TERM signal caught. Exiting..."' TERM HUP QUIT +trap 'trap_exit "Aborted by user! Exiting..."' INT +trap 'trap_exit "An unknown error has occurred. Exiting..."' ERR + +source_pkgbuild() { + # Source this PKGBUILD, if it doesn't exist, exit + unset pkgbase pkgname depends makedepends + unset pkgrel pkgver epoch + if ! source ./PKGBUILD &>/dev/null ; then + error "No PKGBUILD in %s" "$PWD" + exit 1 + fi + + # Save resources + unset pkgdesc license groups backup install md5sums sha1sums \ + sha256sums source options &>/dev/null + + unset build package &>/dev/null + + for _pkg in ${pkgname[@]}; do + unset package_${_pkg} &>/dev/null || true + done + + # This is the name of the package + name="${pkgbase:-${pkgname[0]}}" +} + +source_pkgbuild + +# A temporary work dir and log file +temp_dir="${1:-$(mktemp -dt ${name}-testpkg-XXXX)}" +log="${temp_dir}/buildorder" + +# Generate the full version with epoch +get_fullver() { + if [ $1 -eq 0 ]; then +# zero epoch case, don't include it in version + echo $2-$3 + else + echo $1:$2-$3 + fi + +} + +# Mark array for DFS-based topological sort. See +# https://en.wikipedia.org/wiki/Topological_sort for an explanation of +# the algorithm. Key: package name, value: 0 for unvisited package, 1 +# during visit, 2 after visit. +declare -A marks + +# Visit a PKGBUILD for graph building. +visit_pkgbuild() { + # The name of the previous package + prev="${1}" + + local name + source_pkgbuild + + # If it's already built we don't bother + is_built ${pkgname[0]} $(get_fullver ${epoch:-0} ${pkgver} ${pkgrel}) && + return + + # Detect cycle or already visited package + case "${marks[$name]:-0}" in + 1) msg2 "cycle found with %s depending on %s" $prev $name + exit 1;; + 2) return;; + esac + + msg "%s (%s)" ${name} ${prev} + + echo "${arch[@]}" | grep -qw "$CARCH" || + warning "%s isn't ported to %s yet" ${name} ${CARCH} + + # If the envvar I contains this package, ignore it and exit + echo "$I" | grep -qw "$name" && + msg2 "%s ignored" ${name} && + return + + # Mark the package as being visited + marks[$name]=1 + + # Recurse into dependencies + for d in ${depends[@]} ${makedepends[@]}; do + # Cleanup dependency versions + d=$(echo $d | sed "s/[<>=].*//") + + # Where's the pkgbuild? + w=$(toru-where $d) + + # Skip if not available + test -z "$w" && continue + + # Go to this dir + pushd $w &>/dev/null + + visit_pkgbuild "$name" + + popd &>/dev/null + done + + # Mark the package as finished + marks[$name]=2 + # Append it to the reversed list of packages to build. + echo "$name" >> "${log}" +} + +# If we specified a work dir on the cli it means we want to skip +# dependency graph creation and jump to build whatever is there +if [ -z "${1}" ]; then + # Visit the root PKGBUILD to make the graph. + visit_pkgbuild "" +else + msg "Resuming build..." +fi + +# enter work dir +pushd "${temp_dir}" &>/dev/null +nl ${log} | while read order pkg; do + # skip if already built + if test -f "${pkg}/built_ok"; then + warning "tried to build %s twice" "%{pkg}" + continue + fi + + # where's this package? + w="$(toru-where "$pkg")" + test -z "$w" && continue + + # copy to work dir if not already + # this means you can make modifications to the pkgbuild during the + # graph build or remove the dir after a build failure and let dagpkg + # copy a new version + test -d "$pkg" || cp -r "$w" "$pkg" + pushd "$pkg" &>/dev/null + + term_title "$pkg($order)" + + msg "Building %s" ${pkg} + + # upgrade the system + # this would probably have to go on HOOKPREBUILD if you're working + # outside chroots + sudo -E pacman -Syu --noconfirm + + # run the pre build command from libretools.conf + ${HOOKPREBUILD} + + # run the build command + ${FULLBUILDCMD} + + # Run local release hook with $1 = $repo + ${HOOKLOCALRELEASE} "$(basename "$(dirname "$w")")" + + # it's built! + touch built_ok + + popd &>/dev/null +done + +popd &>/dev/null +# cleanup +rm -rf ${log} "${temp_dir}" + +term_title "done" |