# SPDX-License-Identifier: GPL-2.0-or-later # Copyright (C) 2026 Bryan Joshua Pedini # closure-dpkg.awk - dependency closure (Pre-Depends + Depends) of installed packages, computed # from the dpkg database only (exactly the dependencies of the INSTALLED versions; virtual # packages are resolved through the Provides of installed packages; every installed alternative # of an "a | b" group is followed). # usage: dpkg-query -W -f '${db:Status-Abbrev}\t${Package}\t${Pre-Depends}\t${Depends}\t${Provides}\n' \ # | awk -F'\t' -v roots="pkg1 pkg2" -f closure-dpkg.awk # output: plain package names, one per line, roots included (only if installed), BFS order function trim(s) { gsub(/^[ ]+|[ ]+$/, "", s); return s } substr($1, 2, 1) ~ /[nc]/ { next } # not-installed / config-files-only { inst[$2] = 1 deps[$2] = $3 "," $4 n = split($5, pv, ",") for (i = 1; i <= n; i++) { p = trim(pv[i]); sub(/ .*/, "", p); sub(/:.*/, "", p) if (p != "") prov[p] = prov[p] " " $2 } } END { nr = split(roots, r, " ") for (i = 1; i <= nr; i++) if (inst[r[i]] && !seen[r[i]]) { seen[r[i]] = 1; q[++t] = r[i] } for (h = 1; h <= t; h++) { p = q[h]; print p m = split(deps[p], d, ",") for (j = 1; j <= m; j++) { na = split(d[j], alt, "|") for (k = 1; k <= na; k++) { a = alt[k] gsub(/\([^)]*\)/, "", a); gsub(/\[[^]]*\]/, "", a); gsub(/<[^>]*>/, "", a) a = trim(a); sub(/:(any|native)$/, "", a) if (a == "") continue if (inst[a] && !seen[a]) { seen[a] = 1; q[++t] = a } np = split(prov[a], pp, " ") for (x = 1; x <= np; x++) if (!seen[pp[x]]) { seen[pp[x]] = 1; q[++t] = pp[x] } } } } }