Merge pull request #1016 from VorpalBlade/feature/3rd-party-completion
[aurutils.git] / lib / aur-depends
blob0e87340efd737ec0709452a5db55b5a018b4e121
1 #!/bin/bash
2 # aur-depends - retrieve package dependencies using AurJson
3 [[ -v AUR_DEBUG ]] && set -o xtrace
4 argv0=depends
5 PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
7 # default options
8 max_request=30
9 mode=pkgname
10 resolve_depends=1
11 resolve_makedepends=1
12 resolve_checkdepends=1
13 resolve_optdepends=0
15 count() {
16 jq -r '.resultcount' "$1" | awk '{s += $1} END {print s}'
19 tabulate() {
20 jq -r -f /dev/stdin "$1" <<'EOF'
21 .results[]
22 | . as $result
24 # the package itself
25 | [[$result.Name, $result.Name, $result.PackageBase, $result.Version, "Self"]]
27 # enumerate dependencies per kind
29 "Depends", "MakeDepends", "CheckDepends", "OptDepends"
30 | . as $kind
32 | $result[$kind][]?
33 | [$result.Name, ., $result.PackageBase, $result.Version, $kind]
35 | .[]
36 | @tsv
37 EOF
40 # $1 pkgname $2 depends $3 pkgbase $4 pkgver $5 depends_type
41 select_deps() {
42 awk -v deps="$1" -v mdeps="$2" -v cdeps="$3" -v odeps="$4" '
43 $5 ~ /^Self$/ {print}
44 $5 ~ /^Depends$/ && deps {print}
45 $5 ~ /^MakeDepends$/ && mdeps {print}
46 $5 ~ /^CheckDepends$/ && cdeps {print}
47 $5 ~ /^OptDepends$/ && odeps {print}
48 ' "$5"
51 # $1 pkgname $2 depends $3 pkgbase
52 package_graph() {
53 awk -v type="$1" 'FNR == NR {
54 map[$1] = $3
55 next
57 $2 in map {
58 if (type == "pkgname") {
59 printf("%s\t%s\n", $1, $2)
60 } else if (type == "pkgbase") {
61 printf("%s\t%s\n", $3, map[$2])
63 }' "${@:2}"
66 tr_ver() {
67 sed -r 's/[<>=].*$//g'
70 # shellcheck disable=SC2086
71 chain() {
72 local a num sub
73 local select_args=("$1" "$2" "$3" "$4")
74 shift 4
76 aur query -t info "$@" > json/0 || exit
77 num=$(count json/0)
79 if (( num < 1 )); then
80 printf >&2 '%s: no packages found\n' "$argv0"
81 exit 1
84 # In the below, all intermediary results are stored (originally done to
85 # simplify debugging). Strictly speaking, only the current and previous
86 # step are required. With a limited amount of requests (e.g. ~7 total
87 # for large meta-packages such as ros-indigo-desktop and ~250 AUR
88 # dependencies) the difference is unlikely to be noticeable.
89 for (( a = 1; a <= max_request; ++a )); do
90 sub=$(( a - 1 ))
91 tabulate json/$sub | select_deps "${select_args[@]}" | tee -a tsv/n > tsv/$sub
93 # Avoid querying duplicates (#4)
94 cut -f1 tsv/$sub >> seen # pkgname
95 cut -f2 tsv/$sub | tr_ver | grep -Fxvf seen | \
96 aur query -t info - > json/$a || exit # rpc error
98 if [[ -s json/$a ]]; then
99 num=$(count json/$a)
100 else
101 return $a # no unique results (seen \ tsv == [empty])
104 if (( num >= 1 )); then
105 cut -f2 tsv/$sub >> seen # depends
106 else
107 return $a # no results, recursion complete
109 done
111 # recursion limit exceeded
112 return $max_request
115 trap_exit() {
116 if [[ ! -v AUR_DEBUG ]]; then
117 rm -rf -- "$tmp"
118 else
119 printf >&2 'AUR_DEBUG: %s: temporary files at %s\n' "$argv0" "$tmp"
123 usage() {
124 printf >&2 'usage: %s [-abGnt]\n' "$argv0"
125 exit 1
128 source /usr/share/makepkg/util/parseopts.sh
130 opt_short='abGnt'
131 opt_long=('table' 'pkgbase' 'pkgname' 'pkgname-all' 'graph' 'optdepends'
132 'no-depends' 'no-makedepends' 'no-checkdepends')
133 opt_hidden=('dump-options')
135 if ! parseopts "$opt_short" "${opt_long[@]}" "${opt_hidden[@]}" -- "$@"; then
136 usage
138 set -- "${OPTRET[@]}"
140 while true; do
141 case "$1" in
142 -b|--pkgbase)
143 mode=pkgbase ;;
144 -n|--pkgname)
145 mode=pkgname ;;
146 -a|--pkgname-all)
147 mode=pkgname_all ;;
148 -G|--graph)
149 mode=pkgbase_graph ;;
150 -t|--table)
151 mode=table ;;
152 --no-depends)
153 resolve_depends=0 ;;
154 --no-makedepends)
155 resolve_makedepends=0 ;;
156 --no-checkdepends)
157 resolve_checkdepends=0 ;;
158 --optdepends)
159 resolve_optdepends=1 ;;
160 --dump-options)
161 printf -- '--%s\n' "${opt_long[@]}" ${AUR_DEBUG+"${opt_hidden[@]}"}
162 printf -- '%s' "${opt_short}" | sed 's/.:\?/-&\n/g'
163 exit ;;
164 --) shift; break ;;
165 esac
166 shift
167 done
169 # shellcheck disable=SC2174
170 mkdir -pm 0700 "${TMPDIR:-/tmp}/aurutils-$UID"
171 tmp=$(mktemp -d --tmpdir "aurutils-$UID/$argv0.XXXXXXXX") || exit
172 trap 'trap_exit' EXIT
174 rings=0
175 if cd "$tmp" && mkdir json tsv; then
176 # tsv/n: pkgname\tdepends\tpkgbase\pkgver
177 chain "$resolve_depends" "$resolve_makedepends" "$resolve_checkdepends" "$resolve_optdepends" "$@" || rings=$?
178 deptable=tsv/n
180 # check iteration number
181 case $rings in
182 1) true # no dependencies
184 "$max_request")
185 printf >&2 '%s: total requests: %d (out of range)\n' "$argv0" $(( max_request + 1 ))
186 exit 34 ;;
187 esac
189 case $mode in
190 # pkgname (AUR, total order)
191 pkgname)
192 grep -Fxf <(cut -f1 "$deptable") <(cut -f1,2 "$deptable" | tsort | tac)
194 # pkgname (all, total order)
195 pkgname_all)
196 cut -f1,2 "$deptable" | tsort | tac
198 # pkgbase (AUR, total order)
199 pkgbase)
200 package_graph 'pkgbase' "$deptable" "$deptable" | tsort | tac
202 # pkgbase (AUR, graph)
203 pkgbase_graph)
204 package_graph 'pkgbase' "$deptable" "$deptable" | sort -k1b,1 -k1 -u
206 # all information
207 table)
208 cat "$deptable"
211 printf >&2 '%s: invalid argument' "$argv0"
212 exit 22 ;;
213 esac
214 else
215 exit
218 # vim: set et sw=4 sts=4 ft=sh: