diff options
Diffstat (limited to 'src/dagpkg')
-rwxr-xr-x | src/dagpkg | 222 |
1 files changed, 222 insertions, 0 deletions
diff --git a/src/dagpkg b/src/dagpkg new file mode 100755 index 0000000..e1487d5 --- /dev/null +++ b/src/dagpkg @@ -0,0 +1,222 @@ +#!/usr/bin/env bash +# +# dagpkg - create a directed graph of package dependencies and build +# them in topological order + +# Copyright (C) 2014 Nicolás Reynolds <fauno@parabola.nu> +# Copyright (C) 2014 Michał Masłowski <mtjm@mtjm.eu> +# +# License: GNU GPLv3+ +# +# 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 libremessages +source $(librelib conf) + +# Source variables from libretools +load_files libretools +check_vars libretools FULLBUILDCMD || exit 1 +#check_vars libretools HOOKPREBUILD HOOKLOCALRELEASE || exit 1 # optional + +# Source variables from makepkg +load_files makepkg +check_vars makepkg CARCH || exit 1 + +# Globals: +# - temp_dir +# - log +# - name (sort of, it's also local to visit_pkgbuild() ) +# - prev +# - I +# - marks +# - various PKGBUILD variables: +# - pkgbase/pkgname +# - epoch/pkgver/pkgrel +# - arch +# - {,make,check}depends + +# End inmediately but print an useful message +trap_exit() { + local signal=$1; shift + local msg=("$@") + term_title "error!" + echo + error "(%s) %s (leftovers on %s)" \ + "${0##*/}" "$(print "${msg[@]}")" "${temp_dir}" + trap -- "$signal" + kill "-$signal" "$$" +} + +setup_traps trap_exit + +source_pkgbuild() { + # Source this PKGBUILD, if it doesn't exist, exit + if ! load_PKGBUILD &>/dev/null; then + error "No PKGBUILD in %s" "$PWD" + exit 1 + fi + + # Save resources + # This is intentionally less exhaustive than unset_PKGBUILD() + # XXX: document which things we actually *want* to not be unset. + unset pkgdesc license groups backup install md5sums sha1sums \ + sha256sums source options &>/dev/null + + unset build package &>/dev/null + + local _pkg + 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" + +# 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 + if is_built "${pkgname[0]}" "$(get_full_version "${pkgname[0]}")"; then + return + fi + + # 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} + + if ! in_array "${CARCH}" "${arch[@]}"; then + warning "%s isn't ported to %s yet" ${name} ${CARCH} + fi + + # If the envvar I contains this package, ignore it and exit + if in_array "$name" $I; then + msg2 "%s ignored" ${name} + return + fi + + # Mark the package as being visited + marks[$name]=1 + + # Recurse into dependencies + local d + for d in "${depends[@]}" "${makedepends[@]}" "${checkdepends[@]}"; do + # Cleanup dependency versions + d=$(echo $d | sed "s/[<>=].*//") + + # Where's the pkgbuild? + local 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? + local 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 + if [[ -n "$HOOKPREBUILD" ]]; then + ${HOOKPREBUILD} + fi + + # run the build command + ${FULLBUILDCMD} + + # Run local release hook with $1 = $repo + if [[ -n "$HOOKLOCALRELEASE" ]]; then + ${HOOKLOCALRELEASE} "$(basename "$(dirname "$w")")" + fi + + # it's built! + touch built_ok + + popd &>/dev/null +done + +popd &>/dev/null +# cleanup +rm -rf ${log} "${temp_dir}" + +term_title "done" |