--- /dev/null
+Makefile.in
+Makefile
+aclocal.m4
+autom4te.cache
+config.h.in
+config.h
+config.log
+config.status
+configure
+rpmdepsize
+stamp-h1
--- /dev/null
+# rpmdepsize Makefile.am
+# (C) Copyright 2009 Red Hat Inc.
+#
+# 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.
+#
+# This program 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 program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+# Written by Richard W.M. Jones <rjones@redhat.com>
+
+bin_SCRIPTS = \
+ rpmdepsize repodeps
+
+rpmdepsize: rpmdepsize.ml
+ ocamlfind ocamlopt -package sexplib,unix,extlib,sexplib.syntax -syntax camlp4o -linkpkg $< -o $@
+
+repodeps: repodeps.py
+ rm -f $@
+ sed 's,PYTHON,$(PYTHON),' < $< > $@-t
+ chmod 0555 $@-t
+ mv $@-t $@
+
+man_MANS = \
+ rpmdepsize.1
+
+if HAVE_PERLDOC
+
+rpmdepsize.1: rpmdepsize.pl
+ pod2man \
+ --section 1 \
+ -c "Virtualization Support" \
+ --release "$(PACKAGE_NAME)-$(PACKAGE_VERSION)" \
+ $< > $@
+
+endif
+
+EXTRA_DIST = \
+ rpmdepsize.1 \
+ rpmdepsize.sh \
+ repodeps.py
--- /dev/null
+rpmdepsize
+by Richard W.M. Jones <rjones@redhat.com>
+http://et.redhat.com/~rjones/rpmdepsize
+----------------------------------------------------------------------
+
+This program displays the size of RPMs and their dependencies. It's
+useful for shaming RPMs that have too many dependencies or pull in
+large amounts of data because of indirect dependencies.
+
+Please read the manual page rpmdepsize(1) for full details.
+
+Requirements
+----------------------------------------------------------------------
+
+ perl
+
+ python
+
+ yum
+
+ perldoc
+
+ graphviz
+
+ repoquery
+
+Build
+----------------------------------------------------------------------
+
+ ./configure
+ make
+ sudo make install
--- /dev/null
+#!/bin/sh -
+
+set -e
+set -v
+export AUTOMAKE='automake --foreign --add-missing'
+autoreconf
+./configure "$@"
--- /dev/null
+dnl rpmdepsize configure.ac
+dnl (C) Copyright 2009 Red Hat Inc.
+dnl
+dnl This program is free software; you can redistribute it and/or modify
+dnl it under the terms of the GNU General Public License as published by
+dnl the Free Software Foundation; either version 2 of the License, or
+dnl (at your option) any later version.
+dnl
+dnl This program is distributed in the hope that it will be useful,
+dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+dnl GNU General Public License for more details.
+dnl
+dnl You should have received a copy of the GNU General Public License
+dnl along with this program; if not, write to the Free Software
+dnl Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+dnl
+dnl Written by Richard W.M. Jones <rjones@redhat.com>
+
+AC_INIT(rpmdepsize,1.0)
+AM_INIT_AUTOMAKE
+
+AC_PATH_PROG(PERL,[perl],[no])
+if test "x$PERL" = "xno" ; then
+ AC_MSG_FAILURE([perl not found])
+fi
+
+AC_PATH_PROG(PYTHON,[python],[no])
+if test "x$PYTHON" = "xno" ; then
+ AC_MSG_FAILURE([python not found])
+fi
+
+AC_CHECK_PROG(PERLDOC,[perldoc],[perldoc],[no])
+if test "x$PERLDOC" = "xno" ; then
+ AC_MSG_WARN([perldoc not found - install perl to make man pages])
+fi
+AM_CONDITIONAL(HAVE_PERLDOC,[test "$perldoc" != "no"])
+
+AC_CHECK_PROG(DOT,[dot],[dot],[no])
+if test "x$DOT" = "xno" ; then
+ AC_MSG_FAILURE([graphviz not found])
+fi
+
+AC_CHECK_PROG(REPOQUERY,[repoquery],[repoquery],[no])
+if test "x$REPOQUERY" = "xno" ; then
+ AC_MSG_FAILURE([repoquery not found])
+fi
+
+AC_CONFIG_HEADERS([config.h])
+AC_CONFIG_FILES([Makefile])
+AC_OUTPUT
--- /dev/null
+#!PYTHON
+# repodeps - list recursive dependencies of a package in the repo
+# (C) Copyright 2009 Red Hat Inc.
+#
+# 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.
+#
+# This program 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 program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+# Written by Richard W.M. Jones <rjones@redhat.com>
+# Heavily derived from a script by Seth Vidal.
+
+import yum
+import yum.misc
+import sys
+
+yb = yum.YumBase ()
+
+basepkg = yb.pkgSack.returnPackages (patterns=[sys.argv[1]])[0]
+deps = dict ({basepkg:False})
+
+# Recursively find all the dependencies.
+stable = False
+while not stable:
+ stable = True
+ for pkg in deps.keys():
+ if deps[pkg] == False:
+ deps[pkg] = []
+ stable = False
+ for r in pkg.requires:
+ ps = yb.whatProvides (r[0], r[1], r[2])
+ best = yb._bestPackageFromList (ps.returnPackages ())
+ if best.name != pkg.name:
+ deps[pkg].append (best)
+ if not deps.has_key (best):
+ deps[best] = False
+ deps[pkg] = yum.misc.unique (deps[pkg])
+
+# Get the data out of python as fast as possible so we can
+# use a serious language for analysis of the tree.
+print "(%s (" % basepkg
+for pkg in deps.keys():
+ print "((nevra %s) (name %s) (epoch %s) (version %s) (release %s) (arch %s) (size %s)" % (pkg, pkg.name, pkg.epoch, pkg.version, pkg.release, pkg.arch, pkg.installedsize)
+ print "(deps ("
+ for p in deps[pkg]:
+ print "%s " % p,
+ print ")))"
+sys.stdout.write ("))") # suppress trailing \n
+
+# # Function to get the total size of a dependency (ie. size of
+# # package + size of all dependencies).
+# def total(pkg, seen=None):
+# if seen is None:
+# seen = dict()
+# if not seen.has_key (pkg):
+# seen[pkg] = True
+# sum = pkg.installedsize
+# for p in deps[pkg]:
+# sum = sum + total (p, seen)
+# return sum
+# else:
+# return 0
+
+# # To speed things up, calculate the total size of each package.
+# totals = dict ()
+# for pkg in deps.keys():
+# totals[pkg] = total (pkg)
+
+# # Sort the lists of dependencies by total size (largest first).
+# def sort_by_totals(a, b):
+# if totals[a] > totals[b]:
+# return -1
+# if totals[a] == totals[b]:
+# return 0
+# if totals[a] < totals[b]:
+# return 1
+
+# for pkg in deps.keys():
+# deps[pkg].sort (cmp=sort_by_totals)
+
+# # Iterate over the tree and print out the package details.
+# def pr(pkg, indent=0, seen=None):
+# if seen is None:
+# seen = dict()
+# if not seen.has_key (pkg):
+# seen[pkg] = True
+# print '%s%s %s/%s' % (" "*indent, pkg, pkg.installedsize, totals[pkg])
+# for p in deps[pkg]:
+# pr (p, indent+2, seen)
+
+# pr (basepkg)
--- /dev/null
+(* rpmdepsize - visualize the size of RPM dependencies
+ * (C) Copyright 2009 Red Hat Inc.
+ *
+ * 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.
+ *
+ * This program 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 program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Written by Richard W.M. Jones <rjones@redhat.com>
+ *)
+
+open Sexplib
+TYPE_CONV_PATH "."
+
+open ExtList
+open Unix
+open Printf
+
+(* This corresponds to the sexpr that we write out from the
+ * Python code. OCaml will type-check it.
+ *)
+type root_packages = string * packages
+and packages = pkg list
+and pkg = {
+ nevra : string; (* name-[epoch:]version-release.arch *)
+ name : string;
+ epoch : int;
+ version : string;
+ release : string;
+ arch : string;
+ size : int64; (* installed size, excl. dirs *)
+ deps : string list;
+}
+ with sexp
+
+(* Full dependency representation. This is actually a graph because
+ * it contains dependency loops. 'deps list' is a ref because we
+ * update it as we are building it.
+ *)
+type deps = Deps of pkg * deps list ref
+
+(* Final tree representation, loops removed, and everything we want to
+ * display stored in the nodes.
+ *)
+type tree = Tree of pkg * int64 * int64 * tree list
+
+module StringMap = Map.Make (String)
+let (+^) = Int64.add
+let sum = List.fold_left (+^) 0L
+let spaces n = String.make n ' '
+
+let () =
+ (* Run the Python program and read in the generated sexpr. *)
+ let cmd =
+ sprintf "./repodeps %s" (Filename.quote Sys.argv.(1)) in
+ let chan = open_process_in cmd in
+ ignore (input_line chan); (* drop "Loaded plugins" *)
+ let root, pkgs =
+ root_packages_of_sexp (Sexp.of_string (Std.input_all chan)) in
+ (match close_process_in chan with
+ | WEXITED 0 -> ()
+ | WEXITED i -> failwith (sprintf "command exited with status %d" i)
+ | WSIGNALED i | WSTOPPED i ->
+ failwith (sprintf "command stopped with signal %d" i)
+ );
+
+ (* Create the dependency graph, probably contains loops so beware. *)
+ let deps = List.map (fun pkg -> Deps (pkg, ref [])) pkgs in
+ let depsmap =
+ List.fold_left (
+ fun map (Deps (pkg, _) as deps) ->
+ StringMap.add pkg.nevra deps map
+ ) StringMap.empty deps in
+ List.iter (
+ fun (Deps (pkg, deps)) ->
+ let deps' = List.map (fun n -> StringMap.find n depsmap) pkg.deps in
+ deps := List.append !deps deps'
+ ) deps;
+
+ (* For each package, calculate the total installed size of the package,
+ * which includes all subpackages pulled in. So it's what would be
+ * installed if you did 'yum install foo'.
+ *)
+ let total pkg =
+ let seen = ref StringMap.empty in
+ let rec _total = function
+ | Deps (pkg, _) when StringMap.mem pkg.nevra !seen -> 0L
+ | Deps (pkg, { contents = children }) ->
+ seen := StringMap.add pkg.nevra true !seen;
+ pkg.size +^ sum (List.map _total children)
+ in
+ _total (StringMap.find pkg.nevra depsmap)
+ in
+ let totalsmap =
+ List.fold_left (
+ fun map pkg -> StringMap.add pkg.nevra (total pkg) map
+ ) StringMap.empty pkgs in
+
+ (* Create the final display tree. Each node is sorted so that
+ * children with the largest contribution come first (on the left).
+ * We remove packages which are already installed by earlier
+ * (leftward) packages. At each node we also store total size and
+ * size of the additional packages.
+ *)
+ let tree =
+ let seen = ref StringMap.empty in
+ let rec build_tree = function
+ | Deps (pkg, _) when StringMap.mem pkg.nevra !seen -> None
+ | Deps (pkg, { contents = children }) ->
+ (* Sort children by reverse total size. *)
+ let cmp (Deps (p1, _)) (Deps (p2, _)) =
+ let t1 = StringMap.find p1.nevra totalsmap in
+ let t2 = StringMap.find p2.nevra totalsmap in
+ compare t2 t1
+ in
+ let children = List.sort ~cmp children in
+ seen := StringMap.add pkg.nevra true !seen;
+ let children = List.filter_map build_tree children in
+ let total = StringMap.find pkg.nevra totalsmap in
+ let childadditional =
+ let rec sum_child_sizes = function
+ | Tree (pkg, _, _, children) ->
+ List.fold_left (
+ fun size child -> size +^ sum_child_sizes child
+ ) pkg.size children
+ in
+ sum_child_sizes (Tree (pkg, 0L, 0L, children)) in
+ Some (Tree (pkg, total, childadditional, children))
+ in
+ Option.get (build_tree (StringMap.find root depsmap)) in
+
+ (* Display tree. *)
+ let rec display ?(indent=0) = function
+ | Tree (pkg, total, childadditional, children) ->
+ printf "%s%s %Ld/%Ld/%Ld\n"
+ (spaces indent) pkg.nevra pkg.size childadditional total;
+ List.iter (display ~indent:(indent+2)) children
+ in
+ display tree
--- /dev/null
+#!PERL -w
+# rpmdepsize - visualize the size of RPM dependencies
+# (C) Copyright 2009 Red Hat Inc.
+#
+# 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.
+#
+# This program 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 program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+# Written by Richard W.M. Jones <rjones@redhat.com>
+
+use strict;
+
+use Getopt::Long;
+use Pod::Usage;
+
+my $man = 0;
+my $help = 0;
+
+GetOptions ('help|?' => \$help,
+ 'man' => \$man)
+ or pod2usage (2);
+pod2usage (1) if $help || @ARGV == 0;
+pod2usage (-exitstatus => 0, -verbose => 2) if $man;
+
+# Recurse through dependencies until all deps have been found.
+my %deps;
+
+foreach (@ARGV) {
+ $deps{$_} = []
+}
+
+my $stable = 0;
+while (!$stable) {
+ $stable = 1;
+ foreach my $name (sort keys %deps) {
+ if (@{$deps{$name}} == 0) {
+ $stable = 0;
+ add_deps ($name);
+ }
+ }
+}
+
+sub add_deps
+{
+ my $name = shift;
+
+ print "resolving deps in $name ...\n";
+
+# repoquery is incredibly slow. Unfortunately python has a
+# privileged position into the yum databases, and a python
+# script to access this information runs quickly, so this
+# is what the alternate implementation below uses.
+# my $cmd =
+# "repoquery --recursive --resolve -R $name |
+# sort -u | awk -F- '{print \$1}'";
+
+ my $cmd = "./repodeps $name | grep -v '^Loaded plugins:'";
+
+ open RQ, "$cmd |" or die "$cmd: $!";
+ my $n = 0;
+ while (<RQ>) {
+ chomp;
+ push @{$deps{$name}}, $_;
+ $n++;
+ $deps{$_} = [] unless exists $deps{$_};
+ }
+ close RQ;
+ push @{$deps{$name}}, $name if $n == 0;
+}
+
+__END__
+
+=head1 NAME
+
+ rpmdepsize - Visualize the size of RPM dependencies
+
+=head1 SYNOPSIS
+
+ rpmdepsize [--options] package [package ...]
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<--help>
+
+Display short usage message and exit.
+
+=item B<--man>
+
+Display manual page and exit.
+
+=back
+
+=head1 DESCRIPTION
+
+
+
+
+
+=head1 HOME PAGE
+
+L<http://et.redhat.com/~rjones/rpmdepsize>
+
+=head1 SEE ALSO
+
+L<rpm(1)>, L<repoquery(1)>, L<dot(1)>.
+
+=head1 AUTHORS
+
+Richard W.M. Jones <rjones @ redhat . com>
+
+=head1 COPYRIGHT
+
+(C) Copyright 2009 Red Hat Inc.,
+L<http://et.redhat.com/~rjones/febootstrap>.
+
+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.
+
+This program 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 program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+=cut