summaryrefslogtreecommitdiff
path: root/src/dagpkg
diff options
context:
space:
mode:
authorLuke Shumaker <LukeShu@sbcglobal.net>2014-03-05 09:53:37 -0500
committerLuke Shumaker <LukeShu@sbcglobal.net>2014-03-05 09:53:37 -0500
commitc808635fca24c2329c63e397accb1d21a6c05e37 (patch)
tree073b47cf95e994b64cb78572c1d5bd53d69a1924 /src/dagpkg
parentfd2333da99a5f496762b89ba8690f8b0ed3836e6 (diff)
parent3fababaa704ea9cd0ed06c62a9af1b83301b9302 (diff)
Merge branch 'fauno' into fauno-merge
Conflicts: mips64el/mipsrelease treepkg
Diffstat (limited to 'src/dagpkg')
-rwxr-xr-xsrc/dagpkg204
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"