#!/usr/bin/env bash # Queries the ABS # Copyright (C) 2011-2012 Nicolás Reynolds # Copyright (C) 2011 Joshua Ismael Haase Hernández (xihh) # Copyright (C) 2012-2013 Michał Masłowski # Copyright (C) 2013 Luke Shumaker # # 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 . ## TODO # * Add license text # * Create symlinks from pkgbase to pkgname[@] for easy package finding ## GOALS # * Have a searchable database of PKGBUILD metadata # * Have an interface for source-only builds # * Possibility to hook up ABS dirs besides ABSROOT (low priority) # * Tell updates and non available binary packages (working on this) source toru-utils # Saves contents on a named cache # $1 cache name (repo) # $2+ contents function store_cache { cache=$1; shift [ -z "$cache" ] && return 1 cat $@ > ${TORUPATH}/${cache}.cache return $? } # Return cache contents # $1 cache name read_cache() { cat ${TORUPATH}/${1}.cache 2>/dev/null return $? } # Outputs an ordered package-fullpkgver array print_package_array() { echo "$@" | tr " " "\n" | sort -u } # Gets repo.db contents (unordered) # $1 repo get_db_contents() { [ ! -r /var/lib/pacman/sync/$1.db ] && return 0 bsdtar -tf /var/lib/pacman/sync/$1.db | cut -d'/' -f1 | sort -u } # Get the pkgname # pkgname from pkgver separator can be either '-' or ' ' extract_pkgname() { echo "$@" | tr " " "\n" | sed "s/^\(.\+\)[- ][^-]\+-[^-]\+$/\1/" } # Get all the pkgnames from a file # pkgname from pkgver separator can be either '-' or ' ' extract_pkgname_from_file() { sed "s/^\(.\+\)[- ][^-]\+-[^-]\+$/\1/" $1 } # Split pkgnames from pkgvers split_pkgname_from_pkgver() { sed "s/^\(.\+\)-\([^-]\+-[^-]\+\)$/\1 \2/" $1 } # Get the fullpkgver # pkgname from pkgver separator can be either '-' or ' ' extract_fullpkgver() { echo "$@" | tr " " "\n" | sed "s/^.\+[ -]\([^-]\+-[^-]\+\)$/\1/" } # Checks if $1 is a valid repo is_repo() { if ! in_array ${1} ${REPOS[@]}; then $quiet || warning "${1} is not a valid repo (check REPOS array at libretools.conf)" return 1 fi } # Updates the database by finding all PKGBUILDS # Workflow: # * Find all PKGBUILDs on the ABS repo specified # * Get all packages already on package repos # * Compare them # Args: update() { local update_sync_file=false # The PKGBUILDs found local -a pkgbuilds=() # The list of pkgname-fullpkgver local -a packages_in_abs=() local -a pkg_updates=() local -a package_paths=() # Traverse all specified repos for __repo in $@; do # Check if the repo is set as such, otherwise skip is_repo ${__repo} || continue # Fullpath of the repo _repopath=$(readlink -f ${__repo}) # This is the syncfile, stores the last date as content and mtime local lastsyncfile=${TORUPATH}/${__repo}.lastsync # Find all the PKGBUILDs newer than the last update # Update newer, otherwise everything if [[ $force = true || ! -e ${lastsyncfile} ]]; then $quiet || warning "Forcing upgrade" # Get all PKGBUILDs pkgbuilds=($(find ${_repopath} -maxdepth 2 -type f -name 'PKGBUILD')) else # Only find newer than lastsyncfile and read everything else from cache pkgbuilds=($(find ${_repopath} -maxdepth 2 -type f -name 'PKGBUILD' -newer ${lastsyncfile})) packages_in_abs=($(read_cache ${__repo})) $quiet || msg2 "Getting ${#packages_in_abs[@]} packages from cache" fi package_paths=($(read_cache ${__repo}.paths || true)) # Inform how many PKGBUILDS were found and quit immediately if none $quiet || msg "Found $((${#pkgbuilds[*]}-1)) PKGBUILDs to update" # Traverse all found PKGBUILDs for _pkgbuild in ${pkgbuilds[@]}; do # Update the sync file because there are pkgbuilds to update update_sync_file=true # Load PKGBUILD's metadata source ${_pkgbuild} || continue # Guess pkgbase from PKGBUILD's basedir _pkgpath=$(dirname "${_pkgbuild}") _pkgbase=${pkgbase:-${pkgname[0]}} # We won't need this (all unsets are for memory efficiency) unset build package url md5sums install pkgdesc backup options # TODO fill a license list unset license # TODO create source tarballs? unset mksource # TODO solve dependency tree? unset depends makedepends for _pkg in ${pkgname[@]}; do # Keep removing unneeded stuff unset package_${_pkg} >/dev/null 2>&1 || true # Fill the list of packages to find packages_in_abs+=($_pkg-$(get_full_version $_pkg)) package_paths+=($_pkg:$_pkgpath) done # end pkgnames unset pkgbase pkgname pkgver pkgrel source epoch done # end pkgbuilds # Sync! (Only if there was an actual sync) ${update_sync_file} && lastsync ${lastsyncfile} if [ "${lastsyncfile}" -nt "${TORUPATH}/${__repo}.paths.cache" ]; then print_package_array "${package_paths[@]}" > $TMPDIR/paths store_cache ${__repo}.paths $TMPDIR/paths fi # If there isn't an update cache or it's older than the last update, we check if [ "${lastsyncfile}" -nt "${TORUPATH}/${__repo}.updates.cache" ]; then # Get repo database contents packages_in_sync=($(get_db_contents ${__repo})) # Drops arrays into files print_package_array "${packages_in_abs[@]}" > ${TMPDIR}/packages_in_abs print_package_array "${packages_in_sync[@]}" > ${TMPDIR}/packages_in_sync # Work with files unset packages_in_abs package_in_sync # Use a different separator for pkgnames and pkgvers # so we can join them by pkgname (first field) split_pkgname_from_pkgver ${TMPDIR}/packages_in_abs | sort -k1b,1 > ${TMPDIR}/in_abs split_pkgname_from_pkgver ${TMPDIR}/packages_in_sync | sort -k1b,1 > ${TMPDIR}/in_sync $quiet || msg "These packages are available to update" # Join both files by pkgname, the end result is: # pkgname syncver absver join ${TMPDIR}/in_sync ${TMPDIR}/in_abs | \ while read need_line; do _pkg=$(echo "${need_line}" | cut -d' ' -f1) _syncver=$(echo "${need_line}" | cut -d' ' -f2) _absver=$(echo "${need_line}" | cut -d' ' -f3) # If the versions differ we need an update # TODO move this to update query if [ "${_syncver}" != "${_absver}" ]; then $quiet || msg2 "$_pkg update from $_syncver to $_absver" $quiet && echo "$_pkg" # FIXME this works all right but it's unset once the while ends #pkg_updates+=("$_pkg") # Fix for the above problem, but it access the file every time instead of # puting all packages together once echo $_pkg >> ${TMPDIR}/updates fi done # end need_line unset _pkg _syncver _absver need_line # Save the cache store_cache ${__repo} ${TMPDIR}/packages_in_abs # See above FIXME # print_package_array "${updates[@]}" > ${TMPDIR}/updates if [ -r ${TMPDIR}/updates ]; then store_cache ${__repo}.updates ${TMPDIR}/updates fi else $quiet || msg "Reading updates from cache..." read_cache ${__repo}.updates fi done # end repos } # Find all the packages that are missing from the repo dbs (aka not built) missing() { true } ## Finds a PKGBUILD on toru's path cache ## usage: where_is # Look in all caches but pick the first one where_is() { local __repo local _path for __repo in ${REPOS[@]}; do _path=$(grep "^${1}:" "${TORUPATH}/${__repo}.paths.cache" 2>/dev/null | cut -d: -f2) [ -n "${_path}" ] && break done [ -z "$_path" ] && return 1 echo ${_path} } # TODO: clean usage instructions function usage { echo "" echo "$0 [options] repo1 ... repon" echo "" echo "Make a db containing PKGBUILD metadata." echo "" echo "-h : this message" # echo "-a : update all repos at once" echo "-u : update repo information" echo "-q : quiet" echo "-f : rebuild the db even if it is updated" echo "-p : return the path for pkgname" echo "" exit 1 } ## MAIN commands=() repos=() quiet=false force=false while getopts 'haqfpum' arg; do case $arg in h) usage; exit 0 ;; # TODO: Update all repos on $REPOS array # a) update_all_repos ;; q) quiet=true ;; f) force=true ;; u) commands+=(update);; p) shift $(( OPTIND - 1 )) where_is "$1" || exit 1;; m) commands+=(missing);; esac shift $(( OPTIND - 1 )) done TMPDIR=$(mktemp -d) [[ -z ${TMPDIR} ]] && exit 1 ${commands[0]} ${@} exit $?