void-packages/common/scripts/lint-conflicts

199 lines
5.5 KiB
Bash
Executable File

#!/usr/bin/env bash
# Report packages installing same file and not marked with
# conflicts or replaces.
# Without argument, find conflicts between packages in local
# repository at hostdir/binpkgs and packages indexed in xlocate.
# With single path as argument, read that local repository.
# With -a flag, find conflicts between packages indexed in xlocate.
if [ "$#" = 0 ]; then
binpkgs="$PWD/hostdir/binpkgs"
elif [ "$1" = -a ]; then
all=1
elif [ -d "$1" ]; then
binpkgs="$1"
else
echo "Usage:"
echo "$0"
echo " check packages in ./hostdir/binpkgs"
echo "$0 path/to/hostdir/binpkgs"
echo " check packages there"
echo "$0 -a"
echo " check all packages indexed in xlocate"
exit 1
fi
declare -A newly_built conflicts_cache providers_cache pairs owners
repositories=("--repository=${binpkgs}" "--repository=${binpkgs}/nonfree")
rv=0
template_exists() {
[ -f "srcpkgs/$1/template" ]
}
partial_check() {
[ -z "$all" ]
}
providers_of() {
# print the pkgname and packages that `provides` it
local pkgname=$1
if [ "${providers_cache[$pkgname]}" = '' ]; then
local line provider_pkgver provided_pkgver provider_pkgname provided_pkgname
local -A providers
providers[$pkgname]=$pkgname
while read -r line; do
line=${line%%'('*}
provider_pkgver=${line%': '*}
provided_pkgver=${line#*': '}
provider_pkgname=${provider_pkgver%-*}
provided_pkgname=${provided_pkgver%-*}
# comes from $(xbps-query -s $pkgname), so $pkgname can be substring
if [ "$provided_pkgname" = "$pkgname" ]; then
providers[$provider_pkgname]=$provider_pkgname
fi
done < <(xbps-query "${repositories[@]}" -p provides -R -s "$pkgname")
# leading space ensures ${[]} != ''
providers_cache[$pkgname]=" ${providers[*]}"
fi
echo ${providers_cache[$pkgname]}
}
conflicts_of() {
# print list of packages that are _marked_ as conflicting with given one
local pkgname=$1
if [ "${conflicts_cache[$pkgname]}" = '' ]; then
local in_conflict provider
local -A all
while read -r in_conflict; do
in_conflict=${in_conflict%'<'*}
in_conflict=${in_conflict%'>'*}
providers_of "$in_conflict" > /dev/null # executing in same process to fill cache
for provider in $(providers_of "$in_conflict"); do
all[$provider]=$provider
done
done < <(xbps-query "${repositories[@]}" -p conflicts,replaces -R "$pkgname")
# leading space ensures ${[]} != ''
conflicts_cache[$pkgname]=" ${all[*]}"
fi
echo ${conflicts_cache[$pkgname]}
}
conflict_between() {
# exit successfully if packages are _marked_ as conflicting
conflicts_of "$1" > /dev/null # executing in same process to fill cache
case " $(conflicts_of "$1") " in
*" $2 "*) return 0
esac
conflicts_of "$2" > /dev/null # executing in same process to fill cache
case " $(conflicts_of "$2") " in
*" $1 "*) return 0
esac
return 1
}
list_newly_built_files() {
# print one line per file in newly built packages
# each line contains pkgname and file path
local pkgver pkgname
while read -r pkgver; do
pkgname=${pkgver%-*}
xbps-query "${repositories[@]}" -i -f "$pkgname" | sed s'/ -> .*//;'" s/^/$pkgname /"
done < <(xbps-query "${repositories[@]}" -i -R -s '' | cut -d' ' -f 2)
}
list_interesting_files() {
# list files potentially contained in more than one package
# each line contains pkgver/pkgname and file path
if partial_check; then
list_newly_built_files
else
xlocate / | sed s'/ -> .*//' | grep -F -f <(xlocate / | cut -f 2- | sed s'/ -> .*//' | sort | uniq -d)
fi
}
group_by_file_full() {
# create associative array `owners` mapping file to list of packages
# for packages potentially conflicting with newly built ones
local pkgver file pkgname
while read -r pkgver file; do
pkgname=${pkgver%-*}
if template_exists "$pkgname"; then
owners[$file]+=" $pkgname"
fi
done < <(list_interesting_files)
}
group_by_file_partial() {
# create associative array `owners` mapping file to list of packages
# for all packages in xlocate
local pkgname file
## newly built packages
while read -r pkgver; do
pkgname=${pkgver%-*}
newly_built[$pkgname]=$pkgname
done < <(xbps-query "${repositories[@]}" -i -R -s '' | cut -d' ' -f 2)
while read -r pkgname file; do
owners[$file]+=" $pkgname"
done < <(list_newly_built_files)
## rest of repository
while read -r pkgver file; do
pkgname=${pkgver%-*}
if [ -z "${newly_built[$pkgname]}" ] && template_exists "$pkgname"; then
owners[$file]+=" $pkgname"
fi
done < <(xlocate / | sed s'/ -> .*//' | grep -F -f <(list_newly_built_files | cut -d ' ' -f 2-))
}
group_by_pair() {
# find package pairs owning same file and not marked as conflicting
local pkg file a b
while read -r pkg file; do
for a in ${owners[$file]}; do
for b in ${owners[$file]}; do
if ! [ "$a" "<" "$b" ]; then
continue
fi
if partial_check && [ -z "${newly_built[$a]}" ] && [ -z "${newly_built[$b]}" ]; then
continue
fi
if ! conflict_between "$a" "$b"; then
unset pair_files
local -A pair_files
eval "${pairs["$a $b"]}"
pair_files[$file]="$file"
pairs["$a $b"]="${pair_files[@]@A}"
fi
done
done
done < <(list_interesting_files)
}
print_out() {
local pair file
if [ "${#pairs[@]}" = 0 ]; then
echo 1>&2 "No conflicts found in" "${repositories[@]#*=}"
exit 0
fi
while read -r pair; do
rv=1
echo "${pair% *} and ${pair#* } conflict for"
unset pair_files
eval "${pairs[$pair]}"
for file in "${pair_files[@]}"; do
echo " $file"
done | sort
done < <(printf '%s\n' "${!pairs[@]}" | sort)
}
if partial_check; then
group_by_file_partial
else
group_by_file_full
fi
group_by_pair
print_out
exit $rv