diff options
Diffstat (limited to 'src/toru')
-rwxr-xr-x | src/toru/toru | 314 | ||||
-rwxr-xr-x | src/toru/toru-info | 28 | ||||
-rwxr-xr-x | src/toru/toru-path | 48 | ||||
-rwxr-xr-x | src/toru/toru-utils | 76 | ||||
-rwxr-xr-x | src/toru/toru-where | 7 |
5 files changed, 473 insertions, 0 deletions
diff --git a/src/toru/toru b/src/toru/toru new file mode 100755 index 0000000..28f0b8a --- /dev/null +++ b/src/toru/toru @@ -0,0 +1,314 @@ +#!/bin/bash +# Queries the ABS +# License: GPL3 + +## 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 $(dirname $(command -v $0))/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 $? +} + +## +# 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 +} + +# 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 ${epoch:-0} $pkgver $pkgrel)) + 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 <pkgname> +# 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 <pkgname>: 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 $? diff --git a/src/toru/toru-info b/src/toru/toru-info new file mode 100755 index 0000000..523f682 --- /dev/null +++ b/src/toru/toru-info @@ -0,0 +1,28 @@ +#!/bin/bash +# Prints info about a given pkgname +source /etc/libretools.conf + +for _pkg in $@; do + _pkgbuild="$(toru-where $_pkg)" + + if [ -f "$_pkgbuild/PKGBUILD" ]; then + source "$_pkgbuild/PKGBUILD" 2>/dev/null || { + warning "Errors on %s" $_pkg + continue + } + + deps="${depends[@]} ${makedepends[@]} ${checkdepends[@]}" + repo="$(basename $(dirname "$_pkgbuild"))" + + unset build package depends makedepends checkdepends optdepends source md5sums + + msg "%s/%s %s-%s" $repo $_pkg $pkgver $pkgrel + msg2 "$pkgdesc" + msg2 "$url" + msg2 "Depends: ${deps}" + else + warning "%s doesn't exist" $_pkg + fi + + unset pkgname pkgver pkgrel pkgdesc url +done diff --git a/src/toru/toru-path b/src/toru/toru-path new file mode 100755 index 0000000..957f49b --- /dev/null +++ b/src/toru/toru-path @@ -0,0 +1,48 @@ +#!/bin/bash + +source $(dirname $(command -v $0))/toru-utils + +TORUPATH=${T:-${TORUPATH}} +VERBOSE=${V:-false} + +if [ ! -w "$TORUPATH" ]; then + error "Toru's path isn't writable. Please check $TORUPATH" + exit 1 +fi + +LASTSYNCFILE=${TORUPATH}/lastsync.paths +PATHFILE=${TORUPATH}/paths.tch + +if [ ! -e "${PATHFILE}" ]; then + tcamgr create "${PATHFILE}" +fi + +# TODO pass other paths via flags +# ABSROOT has trailing slash +fullrepos=() +for (( i = ${#REPOS[@]}-1 ; i >= 0 ; i-- )); do + ${VERBOSE} && msg "Processing [%s]" ${REPOS[$i]} + fullrepos+=("${ABSROOT}${REPOS[$i]}") +done +pkgbuilds=($(get_pkgbuilds ${fullrepos[@]})) + +msg "Updating path cache" +msg2 "${#pkgbuilds[@]} PKGBUILDs to update" +for _pkgbuild in ${pkgbuilds[@]}; do +# plain "$_pkgbuild" + source ${_pkgbuild} >/dev/null 2>&1 || { + error "${_pkgbuild} contains errors, skipping" + continue + } + + fullpath=$(dirname ${_pkgbuild}) + + for _pkg in ${pkgbase} ${pkgname[@]} ${provides[@]}; do + $VERBOSE && msg2 "${_pkg} -> ${fullpath}" + tcamgr put ${PATHFILE} ${_pkg/[<>=]*} ${fullpath} + done + + unset pkgbase pkgname provides +done + +lastsync ${LASTSYNCFILE} diff --git a/src/toru/toru-utils b/src/toru/toru-utils new file mode 100755 index 0000000..316e6b8 --- /dev/null +++ b/src/toru/toru-utils @@ -0,0 +1,76 @@ +#!/bin/bash + +source /etc/abs.conf +source /etc/libretools.conf + +LASTSYNCFILE=${TORUPATH}/lastsync +FORCE=false +QUIET=false +DEBUG=false + +# usage : in_array( $needle, $haystack ) +function in_array { + [[ $2 ]] || return 1 # Not found + + local needle=$1; shift + local item + + for item in "$@"; do + [[ ${item#@} = $needle ]] && return 0 # Found + done + + return 1 # Not Found +} + +# Stores the lastsync date +lastsync() { + local lastsyncfile + + lastsyncfile=$1 + + [ -e ${lastsyncfile} -a ! -w ${lastsyncfile} ] && { + error "The sync date can't be saved. ${lastsyncfile} isn't writable." + return 1 + } + + date +%s > ${lastsyncfile} + touch ${lastsyncfile} +} + +get_dbs() { + local _db + for _db in /var/lib/pacman/sync/*.db; do + bsdtar tf ${_db} | cut -d'/' -f1 | sort -u + done +} + +# repo paths +get_pkgbuilds() { + pkgbuilds=() + + if [[ $FORCE = true || ! -e ${LASTSYNCFILE} ]]; then + + $QUIET || warning "Forcing upgrade" +# Get all PKGBUILDs + extra="" + else +# Only find newer than lastsyncfile and read everything else from cache + extra=" -newer ${LASTSYNCFILE}" + fi + +# Return all PKGBUILDs found + find $@ -mindepth 2 -maxdepth 3 -type f -name 'PKGBUILD' ${extra} +} + +# End inmediately but print a useful message +trap_exit() { + error "$@" + + exit 1 +} + +# Trap signals from makepkg +set -E +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 diff --git a/src/toru/toru-where b/src/toru/toru-where new file mode 100755 index 0000000..e9ab29d --- /dev/null +++ b/src/toru/toru-where @@ -0,0 +1,7 @@ +#!/bin/bash +# Locates a PKGBUILD dir on toru's path cache +source /etc/libretools.conf + +PATHFILE=${TORUPATH}/paths.tch + +tcamgr get ${PATHFILE} $1 2>/dev/null || echo "" |