From 1352b2fb942febad68b8ae6f01a2cd56194feb75 Mon Sep 17 00:00:00 2001 From: Axel Beckert Date: Wed, 9 Nov 2016 01:26:57 +0100 Subject: Import Debian version 1.0.10+nmu1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit debtree (1.0.10+nmu1) unstable; urgency=medium * Non-maintainer upload. * Remove override_dh_builddeb to no more build with deprecated bzip2 compression. (Closes: #833242) * Guard …."\E" with "no warnings qw(misc);" (Closes: #823279) * Fix typo in man page found by lintian. debtree (1.0.10) unstable; urgency=low [ Thorsten Alteholz ] * [6781a55f] show the size of all packages that depend on a given package [ Anibal Monsalve Salazar ] * [f2b58fdf] Add multiarch-support to skiplist Thanks to Fabian Greffrath for the suggestion Closes: #659223 debtree (1.0.9) unstable; urgency=low * Really fix typo in debtree.1 debtree (1.0.8) unstable; urgency=low * Fix typo in debtree.1 Patch by Jonas Genannt Closes: #650431 debtree (1.0.7) unstable; urgency=low * Add python2.6 and python2.7 to endlist Patch by Jakub Wilk Closes: #616173 * Fix out-of-date-standards-version * Fix maintainer-script-without-set-e * Fix debhelper-overrides-need-versioned-build-depends * Debian source format is 3.0 (native) debtree (1.0.6) unstable; urgency=low [ Frans Pop ] * Fix typos in man page; thanks to A. Costa for the patch. Closes: #573818. * Disable pattern meta characters for package names in regular expressions. Closes: #574647. [ Anibal Monsalve Salazar ] * New maintainer. Frans, we'll miss you. debtree (1.0.5) unstable; urgency=low * Remove ucf-handled config files on purge. With thanks to piuparts testing. debtree (1.0.4) unstable; urgency=low * Take the highest available version for source packages as well. * Only use the installed version of a package if the --show-installed option is active; otherwise always use the highest available version. * Document which version of packages will be used in the man page. * Move debtree web site to http://collab-maint.alioth.debian.org/debtree/. debtree (1.0.3) unstable; urgency=low * Take the highest available version for uninstalled packages instead of the lowest. Thanks to Thomas Wentzel for the report and patch. Closes: #554719. * Add a few more X.Org-related packages to the end list. debtree (1.0.2) unstable; urgency=low * Selectively use AptPkg for build dependencies. AptPkg will return the correct build dependencies for the current or a selected architecture, but cannot return the architecture conditions themselves. Therefore use AptPkg for all cases except for ARCH=all. For that case continue to call out to apt-cache. * Add hurd-i386, kfreebsd-i386, kfreebsd-amd64 as valid architectures. * Make debtree work for any locale. Closes: #547388. debtree (1.0.1) unstable; urgency=low * Fix perl error if package has no dependencies at all. * Add gksu to the end list. debtree (1.0) unstable; urgency=low * Change packaging to use the magic 'dh' command. * Upload to the Debian archive. Closes: #447158. Yay! [ Changes in unofficial release 0.9.9 - Wed, 16 Sep 2009 13:35:58 +0200 ] * Complete conversion to perl by using libapt-pkg-perl instead of call-outs to dctrl-tools, except for getting build dependencies as libapt-pkg-perl does not expose architecture conditions (#546863). [ Changes in unofficial release 0.9.0 - Mon, 14 Sep 2009 19:56:12 +0200 ] * Remove support for printing dependency paths. It only duplicates functionality offered better by 'aptitude why'. * First stage of conversion to perl. Script is now written in perl, but still calls out to external dctrl-tools to get dependency info. * Show other providers for any virtual packages provided by the requested package. * Show order of dependencies on virtual packages by numbering the arrows. [ Changes in unofficial release 0.8.1 - Sat, 12 Sep 2009 17:56:27 +0200 ] * Update script version and copyright in script. * Move git repository to collab-maint on alioth. [ Changes in unofficial release 0.8.0 - Sat, 12 Sep 2009 16:01:49 +0200 ] * Add support for showing reverse dependencies in graphs. * Fix incomplete display of skipped packages (only first was shown). [ Changes in unofficial release 0.7.3 - Sun, 06 Sep 2009 11:24:50 +0200 ] * debian/control: add Vcs-Git field. * Fix a few corner cases where provides and alternatives were not displayed correctly for skip or end packages. There's a minor change in the way alternatives containing skipped packages are displayed. * Extend endlist and update for lenny/squeeze. [ Changes in unofficial release 0.7.2 - Wed, 24 Oct 2007 16:23:34 +0200 ] * Fix informational message listing skipped packages. * Don't show packages that provide a pseudo package if they are in the skip list. * Show pseudo packages provided by the requested package in dependency graphs (not in build dependency graphs). * Show multiple providing packages for a virtual package if there are less than a certain number (default 3); dependencies for the providing packages are not shown. * Add common build dependencies to end list. * Add some development/packaging related packages to skip and end lists; prompted by build dep graph for asterisk. [ Changes in unofficial release 0.7.1 - Thu, 18 Oct 2007 15:41:11 +0200 ] * Print info message with skipped dependencies after generating graph. * Split ToDo items out of script into separate file. * Add man page. [ Changes in unofficial release 0.7.0 - Tue, 02 Oct 2007 19:24:38 +0200 ] * Don't print graph elements for unknown packages when showing dependency paths. * Move the 'skip' and 'end' lists to configuration files using ucf to manage them. The user can install overrides in ~/.debtree. * Add logging functions to unclutter script. * Add new option max-depth which makes it possible to reduce the size and complexity of a graph by maximizing the number of times debtree will recurse into the next level of dependencies. [ Changes in unofficial release 0.6.0 - Sun, 30 Sep 2007 14:58:37 +0200 ] * Add support for generating build dependency graphs. By default a graph for the current architecture is generated. * Add new option no-conflicts; useful for build dependency graphs. * Add new option no-skip. This allows packages that by default are skipped (not included at all) to be shown as "end" packages. * Add new option show-all. This results in the full dependency tree to be generated. The dependencies for every package are expanded and no packages are skipped. * Add new option show-installed. Installed packages are marked with a different color ('honeydew'). For satisfied alternative dependencies only installed alternative packages are installed. * Mark unknown packages with a different color ('oldlace'). This can only be done for packages that are not part of an alternatives set; for those that are, a warning is printed. * For leaf pseudo packages we need to check if they were members of alternative dependencies to avoid stray nodes in a graph (seen with geneweb). * Introduce verbosity options and rationalize messages and error handling. * Add sanity check for presence of dctrl-tools scripts. Thanks to Maximilian Attems for the suggestion. [ Changes in unofficial release 0.5.0 - Fri, 28 Sep 2007 06:28:30 +0200 ] * Initial unofficial release. --- TODO | 12 + debian/changelog | 191 ++++++++ debian/compat | 1 + debian/control | 24 + debian/copyright | 27 ++ debian/dirs | 2 + debian/install | 3 + debian/manpages | 1 + debian/postinst | 16 + debian/postrm | 26 ++ debian/rules | 7 + debian/source/format | 1 + debian/source/options | 2 + debtree | 1218 +++++++++++++++++++++++++++++++++++++++++++++++++ debtree.1 | 298 ++++++++++++ endlist | 147 ++++++ html/aptitude0.html | 66 +++ html/aptitude1.html | 59 +++ html/aptitude2.html | 70 +++ html/aptitude3.html | 60 +++ html/aptitude4.html | 75 +++ html/aptitude5.html | 69 +++ html/aptitude6.html | 60 +++ html/aptitude7.html | 67 +++ html/aptitude8.html | 64 +++ html/debconf0.html | 72 +++ html/debconf1.html | 90 ++++ html/debconf2.html | 65 +++ html/debconf3.html | 66 +++ html/debconf4.html | 70 +++ html/debconf5.html | 63 +++ html/debconf6.html | 60 +++ html/debconf7.html | 97 ++++ html/debconf8.html | 65 +++ html/index.html | 258 +++++++++++ html/usage1.html | 87 ++++ html/usage2.html | 69 +++ html/usage3.html | 120 +++++ html/usage4.html | 86 ++++ run-tests | 131 ++++++ skiplist | 23 + testset/README | 1 + testset/aptitude1.dot | 28 ++ testset/aptitude2.dot | 148 ++++++ testset/aptitude3.dot | 171 +++++++ testset/aptitude4.dot | 158 +++++++ testset/aptitude5.dot | 158 +++++++ testset/aptitude6.dot | 184 ++++++++ testset/aptitude7.dot | 285 ++++++++++++ testset/aptitude8.dot | 157 +++++++ testset/debconf1.dot | 36 ++ testset/debconf2.dot | 42 ++ testset/debconf3.dot | 59 +++ testset/debconf4.dot | 48 ++ testset/debconf5.dot | 48 ++ testset/debconf6.dot | 68 +++ testset/debconf7.dot | 104 +++++ testset/debconf8.dot | 47 ++ testset/dpkg.dot | 16 + testset/test1.dot | 21 + testset/test2.dot | 231 ++++++++++ testset/test3.dot | 120 +++++ testset/test4.dot | 70 +++ testset/usage1.dot | 41 ++ testset/usage2.dot | 95 ++++ testset/usage3.1.dot | 76 +++ testset/usage3.2.dot | 85 ++++ testset/usage4.dot | 54 +++ 68 files changed, 6539 insertions(+) create mode 100644 TODO create mode 100644 debian/changelog create mode 100644 debian/compat create mode 100644 debian/control create mode 100644 debian/copyright create mode 100644 debian/dirs create mode 100644 debian/install create mode 100644 debian/manpages create mode 100644 debian/postinst create mode 100644 debian/postrm create mode 100755 debian/rules create mode 100644 debian/source/format create mode 100644 debian/source/options create mode 100755 debtree create mode 100644 debtree.1 create mode 100644 endlist create mode 100644 html/aptitude0.html create mode 100644 html/aptitude1.html create mode 100644 html/aptitude2.html create mode 100644 html/aptitude3.html create mode 100644 html/aptitude4.html create mode 100644 html/aptitude5.html create mode 100644 html/aptitude6.html create mode 100644 html/aptitude7.html create mode 100644 html/aptitude8.html create mode 100644 html/debconf0.html create mode 100644 html/debconf1.html create mode 100644 html/debconf2.html create mode 100644 html/debconf3.html create mode 100644 html/debconf4.html create mode 100644 html/debconf5.html create mode 100644 html/debconf6.html create mode 100644 html/debconf7.html create mode 100644 html/debconf8.html create mode 100644 html/index.html create mode 100644 html/usage1.html create mode 100644 html/usage2.html create mode 100644 html/usage3.html create mode 100644 html/usage4.html create mode 100755 run-tests create mode 100644 skiplist create mode 100644 testset/README create mode 100644 testset/aptitude1.dot create mode 100644 testset/aptitude2.dot create mode 100644 testset/aptitude3.dot create mode 100644 testset/aptitude4.dot create mode 100644 testset/aptitude5.dot create mode 100644 testset/aptitude6.dot create mode 100644 testset/aptitude7.dot create mode 100644 testset/aptitude8.dot create mode 100644 testset/debconf1.dot create mode 100644 testset/debconf2.dot create mode 100644 testset/debconf3.dot create mode 100644 testset/debconf4.dot create mode 100644 testset/debconf5.dot create mode 100644 testset/debconf6.dot create mode 100644 testset/debconf7.dot create mode 100644 testset/debconf8.dot create mode 100644 testset/dpkg.dot create mode 100644 testset/test1.dot create mode 100644 testset/test2.dot create mode 100644 testset/test3.dot create mode 100644 testset/test4.dot create mode 100644 testset/usage1.dot create mode 100644 testset/usage2.dot create mode 100644 testset/usage3.1.dot create mode 100644 testset/usage3.2.dot create mode 100644 testset/usage4.dot diff --git a/TODO b/TODO new file mode 100644 index 0000000..6274224 --- /dev/null +++ b/TODO @@ -0,0 +1,12 @@ +ToDo list for debtree +--------------------- +* add "usage" function and --version and --help options +* this code really needs a testsuite as regressions are many and subtle; + comparing the .dot output with expected would be simpler if a fixed + status database was used +* complete conversion to perl by using libapt-pkg-perl instead of calls + to dctrl-tools +* automatic limiting +* automatically split graphs over multiple subgraphs (which dot will place + on separate pages) +* identify transitional packages diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..dacdcb6 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,191 @@ +debtree (1.0.10+nmu1) unstable; urgency=medium + + * Non-maintainer upload. + * Remove override_dh_builddeb to no more build with deprecated bzip2 + compression. (Closes: #833242) + * Guard …."\E" with "no warnings qw(misc);" (Closes: #823279) + * Fix typo in man page found by lintian. + + -- Axel Beckert Wed, 09 Nov 2016 01:26:57 +0100 + +debtree (1.0.10) unstable; urgency=low + + [ Thorsten Alteholz ] + * [6781a55f] show the size of all packages that depend on a given package + + [ Anibal Monsalve Salazar ] + * [f2b58fdf] Add multiarch-support to skiplist + Thanks to Fabian Greffrath for the suggestion + Closes: #659223 + + -- Anibal Monsalve Salazar Fri, 22 Jun 2012 09:17:00 +1000 + +debtree (1.0.9) unstable; urgency=low + + * Really fix typo in debtree.1 + + -- Anibal Monsalve Salazar Wed, 30 Nov 2011 09:59:04 +1100 + +debtree (1.0.8) unstable; urgency=low + + * Fix typo in debtree.1 + Patch by Jonas Genannt + Closes: #650431 + + -- Anibal Monsalve Salazar Wed, 30 Nov 2011 09:43:38 +1100 + +debtree (1.0.7) unstable; urgency=low + + * Add python2.6 and python2.7 to endlist + Patch by Jakub Wilk + Closes: #616173 + * Fix out-of-date-standards-version + * Fix maintainer-script-without-set-e + * Fix debhelper-overrides-need-versioned-build-depends + * Debian source format is 3.0 (native) + + -- Anibal Monsalve Salazar Sun, 17 Apr 2011 09:42:13 +1000 + +debtree (1.0.6) unstable; urgency=low + + [ Frans Pop ] + * Fix typos in man page; thanks to A. Costa for the patch. Closes: #573818. + * Disable pattern meta characters for package names in regular expressions. + Closes: #574647. + + [ Anibal Monsalve Salazar ] + * New maintainer. Frans, we'll miss you. + + -- Anibal Monsalve Salazar Sat, 04 Sep 2010 23:18:01 +1000 + +debtree (1.0.5) unstable; urgency=low + + * Remove ucf-handled config files on purge. With thanks to piuparts testing. + + -- Frans Pop Tue, 09 Feb 2010 17:49:44 +0100 + +debtree (1.0.4) unstable; urgency=low + + * Take the highest available version for source packages as well. + * Only use the installed version of a package if the --show-installed option + is active; otherwise always use the highest available version. + * Document which version of packages will be used in the man page. + * Move debtree web site to http://collab-maint.alioth.debian.org/debtree/. + + -- Frans Pop Sat, 14 Nov 2009 15:35:46 +0100 + +debtree (1.0.3) unstable; urgency=low + + * Take the highest available version for uninstalled packages instead of the + lowest. Thanks to Thomas Wentzel for the report and patch. Closes: #554719. + * Add a few more X.Org-related packages to the end list. + + -- Frans Pop Sat, 07 Nov 2009 03:19:16 +0100 + +debtree (1.0.2) unstable; urgency=low + + * Selectively use AptPkg for build dependencies. AptPkg will return the + correct build dependencies for the current or a selected architecture, + but cannot return the architecture conditions themselves. Therefore use + AptPkg for all cases except for ARCH=all. For that case continue to call + out to apt-cache. + * Add hurd-i386, kfreebsd-i386, kfreebsd-amd64 as valid architectures. + * Make debtree work for any locale. Closes: #547388. + + -- Frans Pop Sun, 20 Sep 2009 12:09:17 +0200 + +debtree (1.0.1) unstable; urgency=low + + * Fix perl error if package has no dependencies at all. + * Add gksu to the end list. + + -- Frans Pop Thu, 17 Sep 2009 23:24:02 +0200 + +debtree (1.0) unstable; urgency=low + + * Change packaging to use the magic 'dh' command. + * Upload to the Debian archive. Closes: #447158. Yay! + + [ Changes in unofficial release 0.9.9 - Wed, 16 Sep 2009 13:35:58 +0200 ] + * Complete conversion to perl by using libapt-pkg-perl instead of call-outs + to dctrl-tools, except for getting build dependencies as libapt-pkg-perl + does not expose architecture conditions (#546863). + + [ Changes in unofficial release 0.9.0 - Mon, 14 Sep 2009 19:56:12 +0200 ] + * Remove support for printing dependency paths. It only duplicates + functionality offered better by 'aptitude why'. + * First stage of conversion to perl. Script is now written in perl, but + still calls out to external dctrl-tools to get dependency info. + * Show other providers for any virtual packages provided by the requested + package. + * Show order of dependencies on virtual packages by numbering the arrows. + + [ Changes in unofficial release 0.8.1 - Sat, 12 Sep 2009 17:56:27 +0200 ] + * Update script version and copyright in script. + * Move git repository to collab-maint on alioth. + + [ Changes in unofficial release 0.8.0 - Sat, 12 Sep 2009 16:01:49 +0200 ] + * Add support for showing reverse dependencies in graphs. + * Fix incomplete display of skipped packages (only first was shown). + + [ Changes in unofficial release 0.7.3 - Sun, 06 Sep 2009 11:24:50 +0200 ] + * debian/control: add Vcs-Git field. + * Fix a few corner cases where provides and alternatives were not displayed + correctly for skip or end packages. There's a minor change in the way + alternatives containing skipped packages are displayed. + * Extend endlist and update for lenny/squeeze. + + [ Changes in unofficial release 0.7.2 - Wed, 24 Oct 2007 16:23:34 +0200 ] + * Fix informational message listing skipped packages. + * Don't show packages that provide a pseudo package if they are in the skip + list. + * Show pseudo packages provided by the requested package in dependency + graphs (not in build dependency graphs). + * Show multiple providing packages for a virtual package if there are less + than a certain number (default 3); dependencies for the providing packages + are not shown. + * Add common build dependencies to end list. + * Add some development/packaging related packages to skip and end lists; + prompted by build dep graph for asterisk. + + [ Changes in unofficial release 0.7.1 - Thu, 18 Oct 2007 15:41:11 +0200 ] + * Print info message with skipped dependencies after generating graph. + * Split ToDo items out of script into separate file. + * Add man page. + + [ Changes in unofficial release 0.7.0 - Tue, 02 Oct 2007 19:24:38 +0200 ] + * Don't print graph elements for unknown packages when showing dependency + paths. + * Move the 'skip' and 'end' lists to configuration files using ucf to + manage them. The user can install overrides in ~/.debtree. + * Add logging functions to unclutter script. + * Add new option max-depth which makes it possible to reduce the size and + complexity of a graph by maximizing the number of times debtree will + recurse into the next level of dependencies. + + [ Changes in unofficial release 0.6.0 - Sun, 30 Sep 2007 14:58:37 +0200 ] + * Add support for generating build dependency graphs. By default a graph for + the current architecture is generated. + * Add new option no-conflicts; useful for build dependency graphs. + * Add new option no-skip. This allows packages that by default are skipped + (not included at all) to be shown as "end" packages. + * Add new option show-all. This results in the full dependency tree to be + generated. The dependencies for every package are expanded and no packages + are skipped. + * Add new option show-installed. Installed packages are marked with a + different color ('honeydew'). For satisfied alternative dependencies only + installed alternative packages are installed. + * Mark unknown packages with a different color ('oldlace'). This can only + be done for packages that are not part of an alternatives set; for those + that are, a warning is printed. + * For leaf pseudo packages we need to check if they were members of + alternative dependencies to avoid stray nodes in a graph (seen with + geneweb). + * Introduce verbosity options and rationalize messages and error handling. + * Add sanity check for presence of dctrl-tools scripts. + Thanks to Maximilian Attems for the suggestion. + + [ Changes in unofficial release 0.5.0 - Fri, 28 Sep 2007 06:28:30 +0200 ] + * Initial unofficial release. + + -- Frans Pop Wed, 16 Sep 2009 13:55:56 +0200 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..7f8f011 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +7 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..3393ecd --- /dev/null +++ b/debian/control @@ -0,0 +1,24 @@ +Source: debtree +Section: utils +Priority: optional +Maintainer: Anibal Monsalve Salazar +Build-Depends: debhelper (>= 7.0.50~) +Standards-Version: 3.9.2 +Homepage: http://collab-maint.alioth.debian.org/debtree/ +Vcs-Git: git://git.debian.org/collab-maint/debtree.git +Vcs-Browser: http://git.debian.org/?p=collab-maint/debtree.git + +Package: debtree +Architecture: all +Depends: ${misc:Depends}, ${perl:Depends}, libapt-pkg-perl, ucf, dctrl-tools +Recommends: graphviz +Description: package dependency graphs on steroids + Very powerful and versatile tool for generating dependency graphs showing the + relationships between .deb packages. + . + The graph is generated in the form of a .dot file that can be used as input + for the utility 'dot' from the graphviz package. What information is included + in a graph can be varied using command line options. + . + Supported are: regular (forward) dependencies, reverse dependencies, showing + installed packages, virtual packages, alternatives, and much more. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..7199023 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,27 @@ +Author: + Frans Pop + +Copyright: + Copyright (C) 2007-2009 Frans Pop + +License: + + This package 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 2 of the License, or + (at your option) any later version. + + This package 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this package; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +On Debian systems, the complete text of the GNU General Public License can +be found in `/usr/share/common-licenses/GPL-2'. + +The Debian packaging is (C) 2007-2009, Frans Pop and +is also licensed under the GPL, see above. diff --git a/debian/dirs b/debian/dirs new file mode 100644 index 0000000..39e7987 --- /dev/null +++ b/debian/dirs @@ -0,0 +1,2 @@ +/etc/debtree +/usr/share/debtree diff --git a/debian/install b/debian/install new file mode 100644 index 0000000..e7b34aa --- /dev/null +++ b/debian/install @@ -0,0 +1,3 @@ +debtree /usr/bin/ +skiplist /usr/share/debtree/ +endlist /usr/share/debtree/ diff --git a/debian/manpages b/debian/manpages new file mode 100644 index 0000000..2016a8f --- /dev/null +++ b/debian/manpages @@ -0,0 +1 @@ +debtree.1 diff --git a/debian/postinst b/debian/postinst new file mode 100644 index 0000000..dadfd42 --- /dev/null +++ b/debian/postinst @@ -0,0 +1,16 @@ +#! /bin/sh + +set -e + +CONFSRC="/usr/share/debtree" +CONFDEST="/etc/debtree" + +case $1 in + configure) + for file in skiplist endlist; do + ucf --three-way --debconf-ok $CONFSRC/$file $CONFDEST/$file + done + ;; +esac + +#DEBHELPER# diff --git a/debian/postrm b/debian/postrm new file mode 100644 index 0000000..d1ad73f --- /dev/null +++ b/debian/postrm @@ -0,0 +1,26 @@ +#! /bin/sh + +set -e + +CONFDEST="/etc/debtree" + +case $1 in + purge) + for file in skiplist endlist; do + # remove configuration files with dpkg backup extensions + for ext in '~' '%' .bak .ucf-new .ucf-old .ucf-dist; do + rm -f $CONFDEST/$file$ext + done + + # remove the configuration file itself + rm -f $CONFDEST/$file + + # clear it out from the ucf database + if which ucf >/dev/null; then + ucf --purge $CONFDEST/$file + fi + done + ;; +esac + +#DEBHELPER# diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..657234c --- /dev/null +++ b/debian/rules @@ -0,0 +1,7 @@ +#!/usr/bin/make -f + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +%: + dh $@ diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..89ae9db --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/debian/source/options b/debian/source/options new file mode 100644 index 0000000..779dbfd --- /dev/null +++ b/debian/source/options @@ -0,0 +1,2 @@ +compression = "bzip2" +compression-level = 9 diff --git a/debtree b/debtree new file mode 100755 index 0000000..3b703ec --- /dev/null +++ b/debtree @@ -0,0 +1,1218 @@ +#! /usr/bin/perl -w + +# debtree version 1.0.2 +# Copyright 2007-2009 Frans Pop +# 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 2 of the License, or (at your option) +# any later version. + +# Usage: debtree [options] + +# The script produces a dot file for processing with 'dot'. +# Examples: debtree > .dot +# debtree | dot -Tpng -o .png +# debtree | dot -Tps | kghostview - + +sub usage { + warn join(" ", @_)."\n" if @_; + warn < \$show_installed, + 'show-rdeps|R' => \$show_rdeps, + 'build-dep|b' => \$build_dep, + 'alternatives!' => \$with_alternatives, + 'recommends!' => \$with_recommends, + 'conflicts!' => \$with_conflicts, + 'provides!' => \$with_provides, + 'max-providers=i' => \$max_providers, + 'with-suggests|S' => \$with_suggests, + 'versioned-conflicts|VC' => \$with_vconflicts, + 'versions!' => \$with_versions, + 'max-depth=i' => \$max_depth, + 'rdeps-depth=i' => \$rdeps_depth, + 'max-rdeps=i' => \$max_rdeps, + 'skip!' => \$no_skip, + 'show-all' => \$show_all, + 'rotate|r' => \$do_rotate, + 'condense' => \$do_condense, + 'arch=s' => \$ARCH, + 'quiet|q' => \$quiet, + 'verbose|v+' => \$verbose, +) or usage; +#usage if $help; + +# Package for which to generate the graph +my $requested_package = shift or error("invalid number of arguments"); + +if (@ARGV) { + #error("invalid number of arguments"); + print(STDERR "Listing of dependency paths is no longer supported.\n"); + print(STDERR "Use 'aptitude why' instead.\n"); + exit 1; +} + +# Raise verbosity level by one unless the quiet option was passed +$verbose++ unless $quiet; + +# Don't allow Suggests without Recommends +$with_suggests = 0 unless $with_recommends; +# Don't allow versioned Conflicts without Conflicts +$with_vconflicts = 0 unless $with_conflicts; + +# Build-dep implies no-suggests and no-alternatives (unless installed packages +# are being displayed); recommends are optional; rdeps are not allowed +if ($build_dep) { + $ptype = "S"; + $with_suggests = 0; + $show_rdeps = 0; + $with_alternatives = 0 unless $show_installed; + $with_provides = 0; +} else { + $ptype = "B"; +} +error("invalid value for option 'arch'") if ($ARCH && ! grep (/^$ARCH$/, @ARCHES)); + +# These options are overlapping +$rdeps_depth = 1 if ($show_rdeps && $rdeps_depth == 0); +$show_rdeps = 1 if ($rdeps_depth); + +error("invalid value for option 'max-providers'") if ($max_providers < 1); +error("invalid value for option 'max-depth'") if ($max_depth < 0); +error("invalid value for option 'rdeps-depth'") if ($show_rdeps && $rdeps_depth < 1); +error("invalid value for option 'max-rdeps'") if ($max_rdeps < 1); + +# The dependency types to display in the graph +my @dtypes = qw/PreDepends Depends/; +push(@dtypes, "Recommends") if $with_recommends; +push(@dtypes, "Suggests") if $with_suggests; +push(@dtypes, "Conflicts") if $with_conflicts; +# Same for reverse dependencies +my @rdtypes = qw/PreDepends Depends/; +push(@rdtypes, "Recommends") if $with_recommends; + +# ToDo: use configurable colors for everything? +my $rdep_color; +if ($show_installed) { + $rdep_color = "azure"; +} else { + $rdep_color = "ivory"; +} + +## Initialize AptPkg's interface + +use AptPkg::Config '$_config'; +use AptPkg::System '$_system'; +use AptPkg::Source; +use AptPkg::Cache; +$_config->init(); +$_system = $_config->system(); + +if ($verbose) { + # Redirect AptPkg's little ditty so it doesn't go into our files + open(OLDOUT, ">&STDOUT"); + open(STDOUT, ">&STDERR"); + select(STDERR); $| = 1; +} else { + $_config->{quiet} = 2; +} + +# Create instances to search for Depends and, if needed, Build-Depends +my $cache = AptPkg::Cache->new(); +my $source; +# AptPkg does not expose architecture conditions, but it does take them +# into account when determining which dependencies to return. So we can +# use AptPkg to show build dependencies for a specific architecture, but +# not when ARCH=all. For that case we call out to apt-cache instead. +# AptPkg defaults to the host system's architecture. +if ($build_dep && $ARCH ne "all") { + $_config->{"APT::Architecture"} = $ARCH if $ARCH; + $source = AptPkg::Source->new(); +} + +if ($verbose) { + # Restore the redirects + close(STDOUT); + open(STDOUT, ">&OLDOUT"); + close(OLDOUT); + select(STDOUT); $| = 0; +} + +### LOG FUNCTIONS ### + +sub mylog { + my ($loglevel, $msg) = @_; + if ($loglevel <= $verbose) { + print(STDERR "$msg\n"); + } +} +sub info { + my ($loglevel, $msg) = @_; + mylog($loglevel, "I: $msg"); +} +sub mywarn { + my ($loglevel, $msg) = @_; + mylog($loglevel, "W: $msg"); +} +sub debug { + my ($loglevel, $msg) = @_; + mylog($loglevel, "D: $msg"); +} +sub error { + my $msg = shift; + mylog(0, "Error: $msg"); + exit 1 +} + +### FUNCTIONS ### + +# Translates AptPkg's (localized) dependency types to what's used by debtree. +# This is only needed for regular dependencies, not build dependencies. +sub get_type { + my $apt_dtype = shift; + return "PreDepends" if ($apt_dtype == AptPkg::Dep::PreDepends); + return "Depends" if ($apt_dtype == AptPkg::Dep::Depends); + return "Recommends" if ($apt_dtype == AptPkg::Dep::Recommends); + return "Suggests" if ($apt_dtype == AptPkg::Dep::Suggests); + return "Conflicts" if ($apt_dtype == AptPkg::Dep::Conflicts); + return "Unsupported"; +} + +sub import_list { + my ($file, $ltype) = @_; + + if (open LIST, "<$file") { + while () { + chomp; + my ($line) = /^\s*([^#]+)/; + $line =~ s/\s+$// if $line; + next unless $line; + for my $package (split(/ +/, $line)) { + $clist{$package}{$ltype} = 1; + } + } + } + close LIST; +} + +sub get_bdeps_all { + my $package = shift; + + my $pinfo = `apt-cache showsrc $package | grep -E "^Build-Depends(-Indep)?:"`; + chomp $pinfo; + $pinfo =~ s/, /,/g; + $pinfo =~ s/ \| /|/g; + return $pinfo; +} + +sub get_apt_pinfo { + my ($package, $ptype) = @_; + + if ($ptype eq "S") { + my $pdata = $source->get($package); + return unless $pdata; + # First in array is highest version + return shift(@$pdata); + } else { + my $pdata = $cache->get($package); + return unless $pdata; + if ($show_installed && exists $$pdata{CurrentVer}) { + return $$pdata{CurrentVer}; + } elsif (exists $$pdata{VersionList}) { + # First in array is highest version + return shift(@{ $$pdata{VersionList} }); + } + } +} + +sub get_apt_deps { + my ($pinfo, $dtype) = @_; + my $delim=""; + my $deps = ""; + + return "" unless $pinfo; + for my $dep (@{ $$pinfo{DependsList} }) { + next if (get_type(0 + $$dep{DepType}) ne $dtype); + if ($deps) { + $deps .= $delim; + } + $deps .= $$dep{TargetPkg}{Name}; + if (exists $$dep{CompTypeDeb}) { + $deps .= " ($$dep{CompTypeDeb} $$dep{TargetVer})" + } + $delim = ($$dep{CompType} & AptPkg::Dep::Or) ? '|' : ','; + } + #debug(0, "$dtype --- $deps"); + return $deps; +} + +sub is_installed { + my $package = shift; + + my $pinfo = $cache->get($package); + return ($pinfo && $$pinfo{CurrentState} eq "Installed"); +} + +sub get_provides { + my $pinfo = shift; + + return unless exists $$pinfo{ProvidesList}; + my @pset = map($_->{Name}, @{ $$pinfo{ProvidesList} }); + return @pset if @pset; +} + +# Check to see that the version we get matches the one that would +# be selected for the rest of the graph. +sub check_version { + my ($package, $version) = @_; + my $pinfo = get_apt_pinfo($package, ""); + return ($$pinfo{VerStr} eq $version); +} + +sub get_rdeps { + my ($package, $parent) = @_; + my %rdeps; + + my $pinfo = $cache->get($package); + return unless ($pinfo && exists $$pinfo{RevDependsList}); + + for my $rdep (@{ $$pinfo{RevDependsList} }) { + my $pname = $$rdep{ParentPkg}{Name}; + my $dtype = get_type(0 + $$rdep{DepType}); + + next unless grep(/^$dtype$/, @rdtypes); + next if ($parent && $pname eq $parent); + next unless check_version($pname, $$rdep{ParentVer}{VerStr}); + next unless (! $show_installed || is_installed($pname)); + # As long as we don't show versions for rdeps, avoid duplicates + # due to double dependencies; for example, gimp has: + # Depends: libgimp2.0 (>= 2.4.7), libgimp2.0 (< 2.4.7-z) + next if grep(/^$pname$/, @{ $rdeps{$dtype} }); + + push(@{ $rdeps{$dtype} }, $pname); + } + + return %rdeps; +} + +sub get_providers { + my $package = shift; + my @providers; + + my $pinfo = $cache->get($package); + return unless $pinfo && exists $$pinfo{ProvidesList}; + + for my $provider (@{ $$pinfo{ProvidesList} }) { + my $pname = $$provider{OwnerPkg}{Name}; + next unless check_version($pname, $$provider{OwnerVer}{VerStr}); + push(@providers, $pname); + } + return @providers; +} + +sub set_name { + my ($plist, $set) = @_; + foreach my $s (sort keys %$set) { + return $s if ($$set{$s}{plist} eq $plist); + } +} + +sub first_set_with_package { + my ($package, $set) = @_; + foreach my $s (sort keys %$set) { + if ($$set{$s}{plist} =~ /(^|,)\Q$package\E(,|$)/) { + return $s; + } + } +} + +sub get_prior_OR_dep { + my $package = shift; + if (! exists $pstatus{$package}{OR_seen}) { + return first_set_with_package($package, \%OR_dep_list); + } +} + +sub mark_installed { + my ($level, $package) = @_; + my $style = "filled"; + + $style = "\"setlinewidth(2),$style\"" if ($level == 1); + printf("\t\"$package\" [style=${style},fillcolor=honeydew];\n"); +} + +sub mark_rdep { + my $package = shift; + printf("\t\"$package\" [style=filled,fillcolor=$rdep_color];\n"); +} + +sub mark_endpackage { + my $package = shift; + if (! get_prior_OR_dep($package) && ! exists $pstatus{$package}{rdep}) { + printf("\t\"$package\" [shape=diamond];\n"); + if ($show_installed && is_installed($package)) { + mark_installed(0, $package); + } + } +} + +sub print_dot_header { + print("digraph \"$requested_package\" {\n"); + print("\trankdir=LR;\n") unless $do_rotate; + print("\tconcentrate=true;\n") if $do_condense; + print("\tnode [shape=box];\n"); +} + +sub print_dot_footer { + print("}\n"); + + # Show which packages were skipped + my @plist; + for my $package (sort keys %pstatus) { + next unless exists $pstatus{$package}{skipped}; + push(@plist, $package); + } + if (@plist) { + info(1, "The following dependencies have been excluded from the graph (skipped):"); + info(1, "@plist"); + # Also list excluded packages as comments in the output + print("// Excluded dependencies:\n"); + print("// @plist\n"); + } +} + +sub print_dot_OR_header { + my ($atype, $OR_name, $has_inst) = @_; + + printf("\t$OR_name [\n"); + print("\t\tshape = \"record\"\n"); + if ($has_inst) { + if ($atype eq "P") { + print("\t\tstyle = \"rounded,filled\"\n"); + } else { + print("\t\tstyle = \"filled\"\n"); + } + print("\t\tfillcolor = \"honeydew\"\n"); + } elsif ($atype eq "P") { + print("\t\tstyle = \"rounded\"\n"); + } + print("\t\tlabel = \""); # no newline +} +sub print_dot_OR_footer { + print("\"\n\t]\n"); +} + +sub print_dot_OR_main { + my ($package, $seen, $first) = @_; + + print(" | ") unless $first; + # Mark packages previously encountered + if ($package eq "...") { + print(" ..."); + } elsif (exists $pstatus{$package}{unknown}) { + print("<$package> ?$package?"); + } elsif ($seen == 2) { + print("<$package> \\{$package\\}"); + } elsif ($seen == 1) { + print("<$package> [$package]"); + } else { + print("<$package> $package"); + } +} + +sub print_alternatives { + my ($atype, $OR_name, $OR_has_inst, $OR_pkgs) = @_; + + my $first = 1; + print_dot_OR_header($atype, $OR_name, $OR_has_inst); + for my $package (split(/,/, $OR_pkgs)) { + # Is this package also part of an earlier set of alternatives? + my $seen = 0; + if (exists $pstatus{$package}{OR_seen}) { + $seen = 1; + } else { + my $t_or = first_set_with_package($package, \%OR_dep_list); + if ($atype eq "D") { + if (exists $clist{$package}{end} || + exists $pstatus{$package}{ended}) { + $seen = 2; + } elsif ($OR_dep_list{$t_or}{plist} ne $OR_pkgs) { + $seen = 1; + } + } else { + if ($t_or) { + $seen = 1; + } elsif (exists $pstatus{$package}{done} && + ! exists $pstatus{$package}{ended}) { + $seen = 1; + } else { + # Not seen; mark as end package + $seen = 2; + } + } + } + + print_dot_OR_main($package, $seen, $first); + $first = 0; + } + print_dot_OR_footer(); +} + +sub cjoin { + my ($str1, $sep, $str2) = @_; + return ($str1 ? ($str1 . $sep) : "") . $str2; +} +sub cjoin_quoted { + my ($str1, $sep, $str2) = @_; + return ($str1 ? ("\"$str1\"" . $sep) : "") . "\"$str2\""; +} + +sub print_dot_dependency { + my $package = shift; + my $OR_name = shift; + my $prevdep_OR_name = shift; + my $dep_package = shift; + my $dtype = shift; + my $pkgdep = shift; + my $rdep = shift; + my ($opts, $verdep, $arch_cond, $p_package, $p_dep_package, $prior_OR_dep); + + # Second or later dependency in alternatives: skip unless versioned (see also below) + # TODO: make this work better if only some of the alternatives have versioned deps + if ($OR_name && (! $with_versions || ! $pkgdep) && + $OR_dep_list{$OR_name}{plist} =~ /,\Q$dep_package\E(,|$)/) { + return + } + + # No extra options if $dtype = Recommends + $opts = cjoin($opts, ",", "color=purple,style=bold") if ($dtype eq "PreDepends"); + $opts = cjoin($opts, ",", "color=blue") if ($dtype eq "Depends"); + $opts = cjoin($opts, ",", "style=dotted") if ($dtype eq "Suggests"); + $opts = cjoin($opts, ",", "color=red") if ($dtype eq "Conflicts"); + $opts = cjoin($opts, ",", "color=darkgoldenrod,style=bold") if ($dtype eq "Build-Depends"); + $opts = cjoin($opts, ",", "color=goldenrod") if ($dtype eq "Build-Depends-Indep"); + + if ($pkgdep) { + ($verdep) = $pkgdep =~ /(\(.*\))/; + ($arch_cond) = $pkgdep =~ /(\[.*\])/; + # Always include versions for versioned conflicts + if ($verdep && ($with_versions || $dtype eq "Conflicts")) { + $opts = cjoin($opts, ",", "label=\"$verdep" . ($arch_cond ? "\\n$arch_cond" : "") . "\""); + } elsif ($arch_cond && $dtype =~ /Build-Depends/) { + $opts = cjoin($opts, ",", "label=\"$verdep\""); + } + } + $opts = $opts ? " [$opts]" : ""; + + $p_package = cjoin_quoted($prevdep_OR_name, ":", $package); + + if ($OR_name) { + # Make the link to the alternatives to the group, unless we have + # to show version or arch condition (see also above) + if (($with_versions && $verdep) || $arch_cond) { + $p_dep_package = cjoin_quoted($OR_name, ":", $dep_package); + } else { + $p_dep_package = "\"$OR_name\""; + } + } else { + # Keep all dependencies on a package with the first alternative + # For now unless package was previously encountered separately, + # in which case we use square brackets in the labels (print_dot_OR_main) + $prior_OR_dep = get_prior_OR_dep($dep_package); + $p_dep_package = cjoin_quoted($prior_OR_dep, ":", $dep_package); + } + + printf("\t$p_package -> $p_dep_package$opts;\n"); + + # How can "end" packages that are part of an OR be identified? + # For now, marked by using braces in the labels (see print_dot_OR_main) + if (exists $clist{$dep_package}{end} && ! $rdep && + ! $OR_name && ! $prior_OR_dep && + ! exists $pstatus{$dep_package}{done}) { + if ($dtype ne "Conflicts") { + debug(4, "Adding '$dep_package' ($dtype) to ended (list)"); + $pstatus{$dep_package}{ended} = 1; + } + $pstatus{$dep_package}{done} = 1; + } +} + +sub print_rdeps_provides { + my $rdep = shift; + my $dtype = shift; + my $package = shift; + my @pset = @_; + + if (! exists $pstatus{$rdep}{prov_rdeps}) { + debug(4, "Provider rdeps: $rdep - $dtype - $package - @pset"); + } else { + debug(4, "Provider rdeps: $rdep is already done"); + return 1 + } + $pstatus{$rdep}{prov_rdeps} = 1; + + my $pinfo = get_apt_pinfo($rdep, ""); + my $deps = get_apt_deps($pinfo, $dtype); + no warnings qw(misc); + my $regex = "(\Q" . join("\E|\Q", @pset) . "\E)"; + use warnings; + for my $dep_or (split(/,/, $deps)) { + next unless $dep_or =~ /(^|\|)$regex([| ]|$)/; + + my @dset; + my $cnt = 0; + for my $d (split(/\|/, $dep_or)) { + $d =~ s/ .*//; # strip version + push(@dset, $d); + $cnt++; + + # Packages not in the pset are leaf packages + if (! grep(/^$d$/, @pset) && ! exists $pstatus{$d}{ended}) { + debug(4, "Provider rdeps: adding '$d' to ended (list)"); + $pstatus{$d}{ended} = 1; + } + } + return 0 if ($cnt == 1); + + my $pdep_set_name = set_name(join(",", @dset), \%pdep_set_list); + if (! $pdep_set_name) { + $pdep_set_cnt++; + $pdep_set_name = "pds" . $pdep_set_cnt; + debug(3, "Provider rdeps: new set '$pdep_set_name' - @dset"); + my $cnt = 0; + for my $p (@dset) { + printf("\t\"$pdep_set_name\" -> \"$p\" [style=dotted,label=\"(".++$cnt.")\"];\n"); + } + printf("\t\"$pdep_set_name\" [shape=circle,label=or];\n"); + $pdep_set_list{$pdep_set_name}{plist} = join(",", @dset); + } + print_dot_dependency($rdep, "", "", $pdep_set_name, $dtype, "", ""); + } + return 1 +} + +sub process_deps { + my $level = shift; + my $package = shift; + my $deps = shift; + my $dtype = shift; + my $prevdep_OR_name = shift; + + return if ($max_depth == 0); + debug(3, "$level - dependencies for '$package' ($dtype): $deps"); + + for my $dep (split(/,/, $deps)) { + my $OR_name; + my (@OR_pkgs, @OR_uninst); + my ($OR_skip, $OR_has_skip, $OR_has_inst, $OR_has_uninst, $OR_has_other); + + if ($dep =~ /\|/ && ($with_alternatives || $show_installed)) { + # Gather information about alternatives + for my $dep_or (split(/\|/, $dep)) { + my ($dep_package) = $dep_or =~ /^([^ ]+) ?/; + if (exists $clist{$dep_package}{skip}) { + $pstatus{$dep_package}{skipped} = 1; + $OR_has_skip = 1; + # Account for installation status + if ($show_installed) { + if (is_installed($dep_package)) { + $OR_has_inst = 1; + } else { + $OR_has_uninst = 1; + } + } + next; + } + + if (! $show_installed) { + push(@OR_pkgs, $dep_package); + } elsif (is_installed $dep_package) { + push(@OR_pkgs, $dep_package); + $OR_has_inst = 1; + } else { + push(@OR_uninst, $dep_package); + $OR_has_uninst = 1; + } + } + # If none of the alternatives is installed, show them all + if ($show_installed && ! $OR_has_inst) { + push(@OR_pkgs, @OR_uninst); + $OR_has_uninst = 0; + } + $OR_skip = 1 unless @OR_pkgs; + $OR_has_other = ($OR_has_skip || $OR_has_uninst); + push(@OR_pkgs, "...") if $OR_has_other; + + # Create alternative dependency "records" for dot graph + if (! $OR_skip && $with_alternatives) { + + # Check if we already have these (exact) alternatives + $OR_name = set_name(join(",", @OR_pkgs), \%OR_dep_list); + if (! $OR_name) { + $OR_dep_cnt++; + $OR_name = "alt" . $OR_dep_cnt; + + debug(3, "Alternatives: new set '$OR_name' - @OR_pkgs"); + for my $dep_package (@OR_pkgs) { + # Has this dependency already been + # processed as a separate package? + if (exists $pstatus{$dep_package}{done} && + ! first_set_with_package($dep_package, \%OR_dep_list) && + ! exists $pstatus{$dep_package}{OR_seen}) { + $pstatus{$dep_package}{OR_seen} = 1; + } + } + + $OR_dep_list{$OR_name}{plist} = join(",", @OR_pkgs); + $OR_dep_list{$OR_name}{has_inst} = $OR_has_inst; + } else { + debug(3, "Alternatives: matches existing set '$OR_name'"); + } + } + } + + if ($OR_skip) { + debug(3, "Alternatives: skipping ('$dep')"); + next; + } + + for my $dep_or (split(/\|/, $dep)) { + my ($dep_package) = $dep_or =~ /^([^ ]+) ?/; + debug(3, "Processing dependency '$package'->'$dep_package'"); + + if ($show_installed && $OR_name && + ! grep(/^$dep_package$/, @OR_pkgs)) { + next; + } + + # Note: for build dependencies pkgdep can include architecture conditions + my ($pkgdep) = $dep_or =~ m/ (.*)/; + + if (exists $clist{$dep_package}{skip}) { + $pstatus{$dep_package}{skipped} = 1; + next; + } elsif ($dtype eq "Conflicts" && ! $with_vconflicts && $pkgdep) { + # Include only unversioned conflicts + next; + } + print_dot_dependency($package, $OR_name, $prevdep_OR_name, $dep_package, $dtype, $pkgdep); + + if (exists $pstatus{$dep_package}{done} || + exists $clist{$dep_package}{end}) { + # do nothing + } else { + # Recurse through dependencies, except for Suggests + # and Conflicts + if ($dtype eq "Suggests") { + if (! exists $pstatus{$dep_package}{done}) { + debug(4, "Adding '$dep_package' ($dtype) to ended"); + $pstatus{$dep_package}{ended} = 1; + $pstatus{$dep_package}{leaf} = 1; + } + } elsif ($dtype eq "Conflicts") { + if (! exists $pstatus{$dep_package}{done}) { + $pstatus{$dep_package}{leaf} = 1; + } + } else { + if ($level < $max_depth) { + process_package($level + 1, $dep_package, $OR_name, ""); + } else { + debug(4, "Adding '$dep_package' ($dtype) to ended (max)"); + $pstatus{$dep_package}{ended} = 1; + } + } + } + + last unless $with_alternatives; + } + } +} + +sub pre_process_deps { + my $level = shift; + my $package = shift; + my $prevdep_OR_name = shift; + my $pinfo = shift; + + my $dtype = ""; + my $delim = ""; + my $deps = ""; + + return unless exists $$pinfo{DependsList}; + for my $dep (@{ $$pinfo{DependsList} }) { + my $new_type = get_type(0 + $$dep{DepType}); + if ($new_type ne $dtype) { + if ($deps) { + process_deps($level, $package, $deps, $dtype, $prevdep_OR_name); + $delim = ""; + $deps = ""; + } + $dtype = $new_type; + } + next unless grep(/^$dtype$/, @dtypes); + if ($deps) { + $deps .= $delim; + } + $deps .= $$dep{TargetPkg}{Name}; + if (exists $$dep{CompTypeDeb}) { + $deps .= " ($$dep{CompTypeDeb} $$dep{TargetVer})" + } + $delim = ($$dep{CompType} & AptPkg::Dep::Or) ? '|' : ','; + } + # Ensure the dependency type encountered last also gets processed + if ($deps) { + process_deps($level, $package, $deps, $dtype, $prevdep_OR_name); + } +} + +sub pre_process_bdeps { + my ($level, $package, $pinfo) = @_; + + if ($ARCH eq "all") { + # $pinfo contains output from apt-cache (see get_bdeps_all) + for my $dtype (qw/Build-Depends Build-Depends-Indep/) { + my ($deps) = $pinfo =~ /^$dtype: (.*)/; + next unless $deps; + process_deps($level, $package, $deps, $dtype, ""); + } + } else { + # $pinfo is a pointer to AptPkg data + return unless exists $$pinfo{BuildDepends}; + for my $dtype (keys %{ $$pinfo{BuildDepends} }) { + my $deps = ""; + for my $dep (@{ $$pinfo{BuildDepends}{$dtype} }) { + if ($deps) { + $deps .= ","; + } + $deps .= @$dep[0]; + if (@$dep[1]) { + $deps .= " (@$dep[1] @$dep[2])" + } + } + process_deps($level, $package, $deps, $dtype, ""); + } + } +} + +sub process_virtual { + my $level = shift; + my $package = shift; + my $prevdep_OR_name = shift; + my $ptype = shift; + my @providers = @_; # must be last + my $prior_OR_dep; + my (@OR_pkgs, @OR_uninst); + my ($OR_name, $OR_has_inst, $OR_has_uninst); + + if (exists $pstatus{$package}{done}) { + debug(3, "$level - virtual package '$package' already done"); + return; + } elsif ($level == 1) { + debug(3, "$level - '@providers' provides '$package'"); + } else { + debug(3, "$level - '$package' provided by: @providers"); + } + + my $p_package = cjoin_quoted($prevdep_OR_name, ":", $package); + + my $prov_cnt = @providers; + if ($prov_cnt == 1) { + my $provider = pop(@providers); + if ($ptype eq "V" && exists $pstatus{$provider}{done}) { + debug(3, "$level - '$package' has no other providers"); + return; + } elsif ($level != 1 && exists $clist{$provider}{skip}) { + $pstatus{$provider}{skipped} = 1; + } else { + $prior_OR_dep = get_prior_OR_dep($provider); + my $p_provider = cjoin_quoted($prior_OR_dep, ":", $provider); + if ($level == 1 && ! $show_rdeps) { + printf("\t$p_provider -> $p_package [arrowhead=inv,color=green];\n"); + } else { + printf("\t$p_package -> $p_provider [dir=back,arrowtail=inv,color=green];\n"); + } + + if (exists $clist{$provider}{end} && ! $prior_OR_dep && + ! exists $pstatus{$provider}{done}) { + debug(4, "Adding '$provider' (provides) to ended (list)"); + $pstatus{$provider}{ended} = 1; + $pstatus{$provider}{done} = 1; + } + + # Process as leaf to show other providers + if ($level == 1 && ! exists $pstatus{$package}{done}) { + process_package(0, $package, "", "V"); + } + + # Recurse to get dependencies for providing package + $pstatus{$package}{done} = 1; + if (! exists $pstatus{$provider}{done} && $ptype ne "L") { + process_package($level + 1, $provider, "", ""); + } + } + } else { + # Note: if there are multiple providing packages, we do + # not recurse into them! + for my $provider (@providers) { + if ($show_installed) { + if (is_installed($provider)) { + push(@OR_pkgs, $provider); + $OR_has_inst = 1; + } else { + push(@OR_uninst, $provider); + $OR_has_uninst = 1; + } + } else { + push(@OR_pkgs, $provider); + } + } + if ($show_installed && ! @OR_pkgs) { + # If none of the alternatives is installed, show them all + push(@OR_pkgs, @OR_uninst); + $OR_has_uninst = 0; + } + push(@OR_pkgs, "...") if $OR_has_uninst; + + # Only show multiple providing packages if there are $max_providers + # or less, or when restricted to installed packages + if ($prov_cnt < $max_providers || ($show_installed && $OR_has_inst)) { + # Check if we already have these (exact) alternative providers + $OR_name = set_name(join(",", @OR_pkgs), \%OR_virt_list); + if (! $OR_name) { + $OR_virt_cnt++; + $OR_name = "virt" . $OR_virt_cnt; + + debug(3, "Providers: new set '$OR_name' - @OR_pkgs"); + $OR_virt_list{$OR_name}{plist} = join(",", @OR_pkgs); + $OR_virt_list{$OR_name}{has_inst} = $OR_has_inst; + } else { + debug(3, "Providers: matches existing set '$OR_name'"); + } + + # Print the arrow now; the record itself is printed later + printf("\t$p_package -> $OR_name [dir=back,arrowtail=inv,color=green];\n"); + } else { + printf("\t$p_package -> \"Pr_$package\" [label=\"-$prov_cnt-\",dir=back,arrowtail=inv,color=green];\n"); + printf("\t\"Pr_$package\" [label=\"...\",style=rounded];\n"); + } + } + + # Green arrow sufficiently identifies virtual package that is part of alternatives + if (! $prevdep_OR_name || exists $pstatus{$package}{OR_seen}) { + printf("\t\"$package\" [shape=octagon];\n") unless ($ptype eq "V"); + } + $pstatus{$package}{done} = 1; +} + +sub process_missing { + my $package = shift; + + my $prior_OR_dep = get_prior_OR_dep($package); + if (! $prior_OR_dep) { + printf("\t\"$package\" [style=filled,fillcolor=oldlace];\n"); + } else { + $pstatus{$package}{unknown} = 1; + } +} + +sub find_rdeps { + my $level = shift; + my $package = shift; + my $parent = shift; + my @provides = @_; + my $has_max = 0; + + info(2, "Level $level: processing reverse dependencies for $package"); + $pstatus{$package}{rdep} = 1; + + my @recurse_rdeps; + my %rdeps = get_rdeps($package, $parent); + for my $dtype (sort keys %rdeps) { + my @rdeps = @{ $rdeps{$dtype} }; + my $cnt = @rdeps; + if ($cnt > 0) { + debug(4, "$level - " . ($show_installed ? "installed " : "") . "$dtype rdeps for '$package' ($cnt): @rdeps"); + } + + my $opts; + if ($level < -1 && $cnt > $max_rdeps) { + $opts = "color=purple,style=bold" if ($dtype eq "PreDepends"); + $opts = "color=blue" if ($dtype eq "Depends"); + $opts = "" if ($dtype eq "Recommends"); + $opts = " [label=\"-$cnt-\",$opts]"; # FIXME if dtype=Recommends ? + printf("\t\"RD_$package\" -> \"$package\" $opts;\n"); + $has_max = 1; + next; + } + + push(@recurse_rdeps, @rdeps); + for my $rdep (@rdeps) { + if (@provides && + print_rdeps_provides($rdep, $dtype, $package, ($parent, @provides))) { + next; + } + + if (! exists $pstatus{$rdep}{done}) { + debug(4, "$level - adding reverse dependency '$rdep'->'$package'"); + print_dot_dependency($rdep, "", "", $package, $dtype, "", 1); + } else { + debug(4, "$level - package '$rdep' is already done"); + } + } + } + + for my $rdep (@recurse_rdeps) { + if (! exists $pstatus{$rdep}{rdep}) { + if (! exists $pstatus{$rdep}{done}) { + mark_rdep($rdep); + } + next unless ($level > -$rdeps_depth); + if ($level == -1) { + find_rdeps($level - 1, $rdep, $package); + } else { + find_rdeps($level - 1, $rdep, ""); + } + } + } + + if ($has_max) { + printf("\t\"RD_$package\" [label=\"...\",style=\"rounded,filled\",fillcolor=$rdep_color];\n"); + } +} + +sub process_package { + my $level = shift; + my $package = shift; + my $prevdep_OR_name = shift; + my $ptype = shift; + my $pinfo; + my $pdata = $cache->get($package); + my $size=0; + + info(2, "Level $level: processing $package" . ($ptype ? " ($ptype)" : "")); + + # something installed? + if (exists $$pdata{CurrentVer}) { + $size=$pdata->{CurrentVer}->{InstalledSize}; + $total_size += $size; + $size=$pdata->{CurrentVer}->{Size}; + $download_size += $size; + info (2,"size of $package: $size / total size: $total_size / download size: $download_size"); + }else { + # is there a list of versions available in cache? + if (exists $$pdata{VersionList}) { + # just use first version + my $first_version=shift(@{ $$pdata{VersionList} }); + $size=$first_version->{InstalledSize}; + $total_size += $size; + $size=$first_version->{Size}; + $download_size += $size; + info (2,"size of first $package: $size / total size: $total_size / download site: $download_size"); + } else { + info (2,"no size available: $package"); + } + } + + if ($ptype ne "L" && exists $pstatus{$package}{ended}) { + debug(4, "Removing '$package' from ended"); + delete $pstatus{$package}{ended}; + } + + if ($ptype eq "S" && $ARCH eq "all") { + # AptPkg does not expose architecture conditions, so use apt-cache + $pinfo = get_bdeps_all($package); + } else { + $pinfo = get_apt_pinfo($package, $ptype); + info(3, "Using package version: " . + (($ptype eq "S") ? $$pinfo{Version} : $$pinfo{VerStr})) + if $pinfo; + } + if ($ptype eq "S" || $ptype eq "B") { + if ($pinfo) { + print_dot_header(); + } else { + error("no dependency info found for '$package'"); + } + } + if (! $pinfo) { + my @providers = get_providers($package); + if (@providers) { + # For leaf packages we need to check if it was an alternative + $prevdep_OR_name = get_prior_OR_dep($package) if ($ptype eq "L"); + + process_virtual($level, $package, $prevdep_OR_name, $ptype, @providers); + } else { + process_missing($package); + } + } + + # Mark as done now to prevent recursion loops + $pstatus{$package}{done} = 1; + + return if ($ptype eq "L" || $ptype eq "V"); + + # Recurse through dependencies + if ($pinfo) { + if ($ptype eq "S") { + pre_process_bdeps($level, $package, $pinfo); + } else { + pre_process_deps($level, $package, $prevdep_OR_name, $pinfo); + } + } + + # Provides and reverse dependencies for the requested package + if ($level == 1 && ($with_provides || $show_rdeps)) { + my @provides = get_provides($pinfo); + if (! $with_provides || ! @provides) { + if ($show_rdeps) { + debug(4, "Find rdeps for the package"); + find_rdeps(-1, $package); + } + } elsif (@provides) { + # Virtual packages + for my $provided (@provides) { + process_virtual($level, $provided, "", "L", ($package)); + } + # Installed reverse dependencies for the package and its provides + if ($show_rdeps) { + debug(4, "Find rdeps for the package & its provides"); + for my $p ($package, @provides) { + find_rdeps(-1, $p, $package, @provides); + } + } + } + } + + if ($show_installed && is_installed($package) && + ! get_prior_OR_dep $package) { + mark_installed($level, $package); + } elsif ($level == 1) { + printf("\t\"$package\" [style=\"setlinewidth(2)\"]\n"); + } + + debug(3, "Level $level: processing $package" . ($ptype ? " ($ptype)" : "") . " - done"); +} + +### MAINLINE ### + +# Import lists to limit processing: +# - "skip" packages are completely ignored: nothing is displayed +# - "end" packages are displayed and marked as such, but we don't recurse +# to determine their dependencies +if (! $show_all) { + my $file; + for $file ("$HOME/.debtree/skiplist", "/etc/debtree/skiplist") { + if (-r $file) { + info(3, "Using list of 'skip' packages from $file"); + if (! $no_skip) { + import_list($file, "skip"); + } else { + # Change skip packages to end packages + import_list($file, "end"); + } + last; + } + } + for $file ("$HOME/.debtree/endlist", "/etc/debtree/endlist") { + if (-r $file) { + info(3, "Using list of 'end' packages from $file"); + import_list($file, "end"); + last; + } + } +} + +# ptype B/S indicates "start" binary/source package +process_package(1, $requested_package, "", $ptype); + +# For packages we did not yet recurse through, check if they are virtual +# packages as we want to show the providing package(s) +info(2, "Processing leaf packages"); +for my $package (sort keys %pstatus) { + next unless exists $pstatus{$package}{leaf}; + if (! exists $pstatus{$package}{done}) { + process_package(0, $package, "", "L"); + } +} + +info(2, "Final processing for graph"); +# Print record definitions for alternatives +for my $OR_name (sort keys %OR_dep_list) { + print_alternatives("D", $OR_name, $OR_dep_list{$OR_name}{has_inst}, $OR_dep_list{$OR_name}{plist}); +} + +# Print record definitions for alternative provides +for my $OR_name (sort keys %OR_virt_list) { + print_alternatives("P", $OR_name, $OR_virt_list{$OR_name}{has_inst}, $OR_virt_list{$OR_name}{plist}); +} + +# Set shape to diamond for packages whose dependencies we did not +# recurse into +for my $package (sort keys %pstatus) { + next unless exists $pstatus{$package}{ended}; + mark_endpackage($package); +} + +print_dot_footer(); + +# does it make sense to show these numbers for rdeps? +if (! $show_rdeps) { + printf("// total size of all shown packages: $total_size\n"); + printf("// download size of all shown packages: $download_size\n"); +} diff --git a/debtree.1 b/debtree.1 new file mode 100644 index 0000000..66b20f7 --- /dev/null +++ b/debtree.1 @@ -0,0 +1,298 @@ +.TH DEBTREE 1 "2009-09-12" "Debian Project" "" + +.SH NAME +debtree \- show relationships between packages + +.SH SYNOPSIS +.B debtree +[\fIoptions\fP] \fIpackage\fP + +.SH DESCRIPTION +Generates dependency graphs (in `dot' syntax) for the specified package. +The output is written to STDOUT and can be used as input for the command +\fBdot\fP(1) from the package `graphviz'. +.PP +Dependency graphs will by default show (pre-)dependencies, recommended +packages, unversioned conflicts, and virtual packages provided by the requested +package. Optionally also suggested packages and versioned conflicts can be +included. +.PP +Besides graphs showing regular dependencies, \fBdebtree\fP can also generate +graphs showing the reverse dependencies of and the build dependencies for a +package. + +.SS Dependency types +The type of dependency between packages is by line type and the color of the +arrow indicating the dependency: + Build-Depends: dark gold, bold + Build-Depends-Indep: light gold + Pre-Depends: purple, bold + Depends: blue + Recommends: black + Suggests: black, dotted + Conflicts: red + Provides: green, inverted arrow +.PP +By default the version requirements for versioned dependencies and conflicts +will be shown alongside the arrow. + +.SS Alternative dependencies +Alternative dependencies will be shown within a single node (a rectangular +shape with horizontal lines separating the packages). +.PP +An alternative dependency will be indicated by a single arrow, unless one or +more of the dependencies are versioned. In that case a separate arrow (ending +at the relevant package) showing the version requirement is drawn. Arrows for +dependencies on a package in a set of alternatives will originate at the +correct package in the set, though in some cases this may be on the separation +line between two alternatives. +.PP +If a package included in an alternative dependency also needs to be displayed +separately or is also part of some other alternative dependency set, its +dependencies will only be included once, with the package's first occurrence. +For the secondary occurrences the package name will be shown between square +brackets: `[...]'. +.PP +See also the \-\-show\-installed option below. + +.SS Virtual packages +Virtual packages will be shown as an octagonal shape with a green inverted +arrow from the providing package(s). +.PP +If only a single package provides the virtual package, this package (and +its dependencies) will be displayed in the graph. +.PP +If there are multiple packages that provide the virtual package, they will +be shown within a single node with rounded corners but only if there are less +than three (or the number set by the \-\-max\-providers option). +If there are more than that number, this will be in