#!/bin/bash # TO TEST: (on find_deps) # * Detect pkgnames by provides, replaces, etc. instead of dir tree source /etc/makepkg.conf source /etc/abs.conf source /etc/libretools.conf source /usr/bin/libremessages # Avoid /libretools dir doesn't exist errors if [ -z $XDG_CONFIG_HOME ]; then error "There's no XDG_CONFIG_HOME var set" OFLINE=true elif [ -r $XDG_CONFIG_HOME/libretools/libretools.conf ]; # set queue_file and ban_file source $XDG_CONFIG_HOME/libretools/libretools.conf queue_file=$XDG_CONFIG_HOME/libretools/queue ban_file=$XDG_CONFIG_HOME/libretools/ban fi ##### START FUNCTIONS ##### function usage { echo "cd to a dir containing a PKGBUILD and run:" echo "$0 [options]" printf "This script will check dependencies, build them if possible " printf "and stage the packages on it's repo." echo echo "OPTIONS:" echo " -h : this message." echo " -a absdir : set absdir as ABSROOT." echo " -b : do not check deps but build. Needs -d" echo " -c : check deps only, do not build." echo " -d build_dir : use this dir to build. Defaults to mktemp." echo " -n : don't update pacman db." echo " -m max_level : check deps until this level" echo " -r \"command\" : use this instead of \"$FULLBUILDCMD\"" echo " -o : work offline (avoid queuing)" # printf " -f pkgname : build even when a package has been built. " # printf " Use it as many times as needed\n" echo } # Queue Management # * Always get the queue list from the server # * Add/Remove from queue # * Check if a package is listed # Get the queue list from the server get_queue() { [[ "$OFFLINE" -eq true ]] && return 0 rsync -e ssh -aq $PARABOLAHOST:mips64el/queue $queue_file >/dev/null 2>&1 || { error "Failed to retrieve queue list" return 1 } } # Put the queue list on the server put_queue() { [[ "$OFFLINE" = true ]] && return 0 rsync -e ssh -aq $queue_file $PARABOLAHOST:mips64el/queue >/dev/null 2>&1 || { error "Failed to put queue list" return 1 } } # Add packages to the queue update_queue() { get_queue echo "$(basename $PWD):$PACKAGER" >> $queue_file put_queue || return $? } # Remove a package from the queue remove_queue() { [[ "$OFFLINE" -eq true ]] && return 0 get_queue grep -Evw "^$(basename $PWD)" ${queue_file} > ${queue_file}2 mv -f ${queue_file}2 ${queue_file} put_queue && return 0 || return $? } # Checks if a package is listed check_queue() { [[ "$OFFLINE" = "true" ]] && return 0 get_queue local packager=$(grep -w "$(basename $PWD)" ${queue_file} | cut -d ':' -f2) [ -n "$packager" -a "$packager" != "$PACKAGER" ] && { warning "$(basename $PWD) is being packaged by $packager. Please wait." return 1 } return 0 } # END Queue Management # ## Build order management ## # Removes a package from the buildorder # $1 package name # $2 buildorder file remove_buildorder() { grep -Evw "${1}" ${2} > ${2}2 mv -f ${2}2 ${2} } # Guesses the repo name according to the parent dir of the current package # assuming the form repo/package/PKGBUILD guess_repo() { basename $(dirname $(pwd)) } ## # usage : get_full_version( $epoch, $pkgver, $pkgrel ) # return : full version spec, including epoch (if necessary), pkgver, pkgrel ## get_full_version() { if [[ $1 -eq 0 ]]; then # zero epoch case, don't include it in version echo $2-$3 else echo $1:$2-$3 fi } # Usage: cleanup [ $(basename $PWD) ] from PKGBUILD dir # cleans the build_dir function cleanup { # Already clean if [ ! -d $build_dir ]; then return 0 elif [ -d $build_dir -a ${#@} -gt 0 ]; then for _dir in $@; do rm -rf $build_dir/$_dir/ done else # Only do full cleanup on level 0 [ $level -eq 0 ] && rm -rf $build_dir/* fi } # Check PKGBUILD and find non built or outdated deps # on ABSROOT which should be abslibre-misp64el function find_deps { ## Check this level. source PKGBUILD local repo=${repo:-$(guess_repo)} local pkgbase=${pkgbase:-${pkgname[0]}} local fullver=$(get_full_version ${epoch} ${pkgver} ${pkgrel}) # If package and correct ${fullver} is built exit # TODO?: If this package is in force_build: skip this step if is_built "${pkgbase}=${fullver}"; then exit 0 fi # Tell which packages are deeper in deps (even if they are on build_dir) # so we can build them first. echo "${level}:${pkgbase}" >> "${build_dir}/BUILDORDER" # if pkgbuild directory is on build_dir, do not copy and exit if [ -d "${build_dir}/${pkgbase}" ]; then exit 0 else # Copy dir to build_dir cp -r ../${pkgbase}/ ${build_dir}/ # Info to eval later echo "repo=$repo" > "${build_dir}/${pkgbase}/.INFO" fi # Inform the current package msg2 "${pkgbase}-${fullver}" ## Check next levels # Clean version checking deps=$(echo "${depends[@]} ${makedepends[@]}" | \ sed "s/[=<>]\+[^ ]\+//g" | \ tr ' ' "\n" | \ sort -u) # Increase build level declare -i next_level=$level+1 for _dep in ${deps[@]}; do for _repo in ${REPOS[@]}; do # try to find $_dep on each repo from dirname if [ -e "${ABSROOT}/${_repo}/${_dep}/PKGBUILD" ]; then pushd "${ABSROOT}/${_repo}/${_dep}" > /dev/null $0 -c -d ${build_dir} -l ${next_level} # Circular deps must fail [ $? -eq 20 ] && return 20 popd > /dev/null break 1 # found, go to next dep # if search pkgname in repo doesn't work # this should find pkgsplits elif _dir=($(find "$ABSROOT/${_repo}" -type f -name PKGBUILD -print0 2>/dev/null | \ "xargs" -0 -e grep -HEw "pkgname=|pkgbase=|provides=" | grep -w "$_dep" 2>&1)); then _dir=$(dirname $(echo $_dir | cut -d: -f1)) plain "guess for $_dep -> $_dir" pushd $_dir > /dev/null $0 -c -d ${build_dir} -l ${next_level} # Circular deps must fail [ $? -eq 20 ] && return 20 popd > /dev/null break 1 # found, go to next dep else echo "dep_not_found:$_dep:$_repo" >> $build_dir/log fi done done unset next_level dir # unset PKGBUILD variables unset pkgname pkgver pkgrel epoch pkgdesc arch url license groups depends \ makedepens checkdepends optdepends provides conflicts replaces backup \ options install changelog source noextract md5sums build check package } function _pkg_build () { pushd ${build_dir} > /dev/null # packages to build are on $buildorder # greater levels must be built first build_packages=($(sort -gr $buildorder | cut -d: -f2)) while [ ${#build_packages[@]} -ge 1 ]; do pushd $build_dir/${build_packages[0]} > /dev/null source PKGBUILD msg2 "${pkgbase:-${pkgname[0]}} $pkgver-$pkgrel" # Check if pkg is being built msg2 "Checking build queue" check_queue || { echo "someone_is_building:$(basename $PWD)" >> $build_dir/log remove_buildorder "$(basename $PWD)" $buildorder continue } # Let everybody know we're building this. msg2 "Updating build queue" update_queue || { warning "Couldn't update the queue, let your partners know about this." } msg2 "Checking for non free deps" pkgbuild-check-nonfree || { # pkgbuild-check-nonfree fails with 15 if there are nonfree deps, # fails with something else if blacklist can't be retrieved if [ $? -eq 15 ]; then # log they have nonfree deps and so didn't build echo "nonfree:$(basename $PWD)" >> $build_dir/log # take out package from $buildorder remove_buildorder "$(basename $PWD)" $buildorder # continue building next package continue fi } msg2 "Building $(basename $PWD)" # this buildcmd is on libretools.conf $FULLBUILDCMD; r=$? case $r in ###### Succesfull Build ###### 0) plain "The build was succesful." source .INFO && [ -n $repo ] && { # Calls a local release script if it's used [ -z $HOOKLOCALRELEASE ] || \ $HOOKLOCALRELEASE $repo *.pkg.tar.?z # Stage for releasing librestage $repo || { echo "unstaged:$(basename $PWD)" >> $build_dir/log } msg "Updating pacman db and packages" sudo pacman -Syu --noconfirm } echo "built:$(basename $PWD)" >> $build_dir/log cleanup "$(basename $PWD)" ;; ###### Failed Build ###### *) error "There were errors while trying to build the package." echo "failed:$(basename $PWD)" >> $build_dir/log ;; esac # Package was built or failed: take it out of $buildorder remove_buildorder "${build_packages[0]}" $buildorder # Take package out from queue remove_queue # Set build_packages before next cycle run build_packages=($(sort -gr $buildorder | cut -d: -f2)) popd > /dev/null done pkgs=($(grep "nonfree:" $build_dir/log)) && { error "Those packages contain nonfree deps:" echo ${pkgs[@]} | tr " " "\n" | cut -d: -f2 } pkgs=($(grep "built:" $build_dir/log)) && { msg "Those packages were built and staged:" echo ${pkgs[@]} | tr " " "\n" | cut -d: -f2 } pkgs=($(grep "failed:" $build_dir/log) && { error "Those packages failed to build:" echo ${pkgs[@]} | tr " " "\n" | cut -d: -f2 } pkgs=($(grep "unstaged:" $build_dir/log) && { error "Those packages couldn't be staged because of missing reponame:" echo ${pkgs[@]} | tr " " "\n" | cut -d: -f2 } popd > /dev/null } # End inmediately but print a useful message trap_exit() { remove_queue error "$@" warning "Leftover files left on $build_dir" exit 1 } ## END FUNCTIONS ## ## Trap signals # From makepkg set -E trap 'cleanup' 0 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 force_build="" level=0 noupdate='n' build_only='n' check_deps_only='n' do_cleanup='n' max_level=21 OFFLINE=false while getopts 'ha:bcCd:l:nm:r:o' arg; do case $arg in h) usage; exit 0 ;; a) ABSROOT="$OPTARG" ;; b) build_only='y' ;; c) check_deps_only='y' ;; C) do_cleanup='y';; # f) force_build+="-f pkgname " ;; d) build_dir="$OPTARG" ;; # hidden option to know what to build first. # if $level > 0 it will not build l) level=$OPTARG ;; n) noupdate='y';; m) max_level=$OPTARG ;; r) FULLBUILDCMD="$OPTARG" ;; o) OFFLINE=true ;; esac done [ ! -r PKGBUILD ] && { error "This isn't a build directory" usage && exit 1 } # Only on level 0 if [ $level -eq 0 ]; then # if build_dir exist use it, else make a build_dir build_dir=${build_dir:-$(mktemp -d /tmp/fullpkg.XXXXXX)} # cleanup if the option was used. [ $do_cleanup == 'y' ] && { cleanup [ $build_only == 'y' ] && exit 0 } # make files for log and buildorder touch $build_dir/{log,BUILDORDER} buildorder=$build_dir/BUILDORDER [ $noupdate = 'n' ] && { msg "Updating pacman db and packages" sudo pacman -Syu --noconfirm } # Build only [ $build_only == 'y' ] && { _pkg_build exit 0 } msg "Checking dependencies" fi ## if $level = 20 it's highly likely there are circular deps [ $level -ge $max_level ] && exit 20 find_deps || { # if find_deps finds circular deps # it should exit with status 20 [ $? -eq 20 ] && { # only show message on level 0 [ $level -eq 0 ] && error "Check for circular deps on $build_dir/BUILDORDER"; } exit 20 } # levels greater than 0 must only check deps [ $check_deps_only = 'y' -o $level -gt 0 ] && exit 0 # check BUILDORDER to not include banned deps and [ $level -eq 0 -a -d $build_dir ] && { # Check for banned deps if [ -w $ban_file -a -r $ban_file ]; then chmod o+rw $ban_file || error "Ban file is not readable/writable ($ban_file)" else rsync -e ssh -aq $PARABOLAHOST:mips64el/ban >/dev/null 2>&1 || { warning "Failed to get ban list" && [ -r $ban_file ] && { # Use local copy of ban file if it is avaliable and continue. search=$(cat ${ban_file} | tr "\n" "|") # Keep track of banned files egrep -w "$search" ${buildorder} >> ${build_dir}/banned # Take banned packages out from buildorder egrep -vw "$search" ${buildorder} > ${buildorder}2 mv -f ${buildorder}2 ${buildorder} unset search } } fi } ## START Building msg "Building packages:" # If the queue file isn't writable go into offline mode [ ! -w $queue_file ] && { error "Can't write queue file" OFFLINE=true } # Add mips64el if missing from arch=() and it isn't an 'any' package if ! grep mips64el PKGBUILD >/dev/null; then plain "Adding mips64el arch" sed -i "s/^\(arch=([^)anym]\+\))/\1 'mips64el')/" "PKGBUILD" fi # Build the package _pkg_build echo msg2 "Check if your system works fine and librerelease if it does" exit 0