#!/usr/bin/env bash # # dagpkg - create a directed graph of package dependencies and build # them in topological order # Copyright (C) 2014 Nicolás Reynolds # Copyright (C) 2014 Michał Masłowski # # 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 . 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 "%s(%s)" "$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"