Import of pkgsrc-2013Q2

This commit is contained in:
2013-09-26 17:14:40 +02:00
commit 785076ae39
74991 changed files with 4380255 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
pkgdepgraph prints out a "dot" language specification of the
inter-dependencies of your installed packages. The "dot" language
is interpreted by the graphviz package to make graphs. There are
several uses for such information.
(1) A graphical representation of information is always good to
look at.
(2) The output itself can be sorted and filtered to provide a list
of packages to delete in order that they can be rebuilt (to replace
out-of-date components).
(3) You can visually estimate the work involved in (or impact of)
removing a given component in order to replace it.

View File

@@ -0,0 +1,11 @@
===========================================================================
$NetBSD: MESSAGE,v 1.1 2004/01/29 22:25:06 snj Exp $
Note that in order to facilitate the use of this package on "client
machines", the dependency of this package upon the "graphviz" package
(located in graphics/graphviz) has been removed. You must install the
"graphviz" package in order to view the graph data that this program
generates, though much of the functionality of this program does not
require you to look at the graphs.
===========================================================================

View File

@@ -0,0 +1,62 @@
# $NetBSD: Makefile,v 1.31 2013/05/31 12:41:46 wiz Exp $
#
DISTNAME= pkgdepgraph-2.8
PKGREVISION= 2
CATEGORIES= pkgtools devel
MASTER_SITES= # empty
DISTFILES= # empty
MAINTAINER= pkgsrc-users@NetBSD.org
HOMEPAGE= ftp://ftp.NetBSD.org/pub/NetBSD/packages/pkgsrc/doc/pkgsrc.html
COMMENT= Visual representation of installed NetBSD packages
# removed so that pkgdepgraph can be used on "client" machines -- @@@
# DEPENDS+= graphviz-[0-9]*:../../graphics/graphviz
WRKSRC= ${WRKDIR}
USE_TOOLS+= perl:run
NO_CONFIGURE= yes
DISTVER= ${DISTNAME:S/pkgdepgraph-//}
.include "../../mk/bsd.prefs.mk"
INSTALLATION_DIRS= bin ${PKGMANDIR}/cat1 ${PKGMANDIR}/man1
do-extract:
${CP} ${FILESDIR}/pkgdepgraph.pl ${WRKSRC}/pkgdepgraph.pl
${CP} ${FILESDIR}/pkgdepgraph.1 ${WRKSRC}/pkgdepgraph.1.in
${CP} ${FILESDIR}/pkgdepgraph.0 ${WRKSRC}/pkgdepgraph.0.in
do-build:
.for file in pkgdepgraph
${SED} -e 's|@PREFIX@|${PREFIX}|g' \
-e 's|@PKG_DBDIR@|${PKG_DBDIR}|g' \
-e 's|@PKGSRCDIR@|${PKGSRCDIR}|g' \
-e 's|@DISTVER@|${DISTVER}|g' \
< ${WRKSRC}/${file}.pl \
> ${WRKSRC}/${file}
.endfor
.for file in pkgdepgraph.1 pkgdepgraph.0
${SED} -e 's|@PKG_DBDIR@|${PKG_DBDIR}|g' \
-e 's|@PKGSRCDIR@|${PKGSRCDIR}|g' \
< ${WRKSRC}/${file}.in \
> ${WRKSRC}/${file}
.endfor
.if ${OPSYS} == "SunOS" || ${OPSYS} == "AIX"
# the pre-generated man-pages are 'mandoc'; these OS need 'man'
nroff -man ${WRKSRC}/pkgdepgraph.1 > ${WRKSRC}/pkgdepgraph.0
.endif
do-install:
${INSTALL_SCRIPT} ${WRKSRC}/pkgdepgraph ${DESTDIR}${PREFIX}/bin/pkgdepgraph
.if !empty(MANINSTALL:Mcatinstall)
${INSTALL_MAN} ${WRKSRC}/pkgdepgraph.0 ${DESTDIR}${PREFIX}/${PKGMANDIR}/cat1
.endif
.if !empty(MANINSTALL:Mmaninstall)
${INSTALL_MAN} ${WRKSRC}/pkgdepgraph.1 ${DESTDIR}${PREFIX}/${PKGMANDIR}/man1
.endif
.include "../../mk/bsd.pkg.mk"

View File

@@ -0,0 +1,4 @@
@comment $NetBSD: PLIST,v 1.1.1.1 2002/11/07 23:18:01 atatat Exp $
bin/pkgdepgraph
man/cat1/pkgdepgraph.0
man/man1/pkgdepgraph.1

View File

@@ -0,0 +1,345 @@
PKGDEPGRAPH(1) NetBSD General Commands Manual PKGDEPGRAPH(1)
NNAAMMEE
ppkkggddeeppggrraapphh - visual representation of installed packages
SSYYNNOOPPSSIISS
ppkkggddeeppggrraapphh [--AAaaCCccDDeeFFffggLLllooRRrrssvv] [--dd _p_k_g___d_b_d_i_r] [--ii _i_m_p_a_c_t] [--KK _p_a_c_k_a_g_e_s]
[--MM _m_a_k_e] [--mm _t_a_r_g_e_t] [--OO _p_a_c_k_a_g_e] [--PP _p_k_g_s_r_c_d_i_r]
[--SS _p_a_c_k_a_g_e] [--tt _t_a_r_g_e_t] [--UU _p_a_c_k_a_g_e] [_d_a_t_a _._._.]
DDEESSCCRRIIPPTTIIOONN
ppkkggddeeppggrraapphh emits a _d_o_t language description of the locally installed
packages, with the inter-dependencies reduced to a minimal set of edges.
It scans each directory under the package database directory for files
named _+_B_U_I_L_D___I_N_F_O and _+_R_E_Q_U_I_R_E_D___B_Y in order to gather its data.
ppkkggddeeppggrraapphh will also read any files passed to it as arguments (or stdin
if it is not connected to a tty), in search of output that resembles that
of either lintpkgsrc(1) --ii, lintpkgsrc(1) --gg, audit-packages(8), or the
output from a previous incantation of this program.
In the latter case, ppkkggddeeppggrraapphh will recolor the graph passed as input
based, as usual, on information about currently installed pkgs. Packages
that are in the input graph, but not currently installed, will be colored
black, those that are installed will be colored green, and those that
remain out of date for some reason will remain red.
The graph defaults to black, but if data from lintpkgsrc(1) --ii or
_a_u_d_i_t_-_p_a_c_k_a_g_e_s is given, or if a specific package is asserted to be out
of date (via the --OO option), then nodes (and edges leading from them)
will be colored in the following manner:
green The package is completely up to date.
orange The package is up to date, but some of its dependencies require
updating.
red The package is out of date or has had a security advisory issued
against it, and should be replaced.
purple The package was not found in the local pkgsrc tree.
blue The package depends on something not found in the local pkgsrc
tree.
black The package has been removed and not yet replaced, or the graph
is uncolored. The difference between an uncolored graph and a
graph that has been recolored almost entirely black as a result
of removing all the packages can be visually detected by the
color of the ppkkggddeeppggrraapphh package with which the graph was recol-
ored.
It is important to note that all installed packages fall into one (or
more) of the following categories:
installed Any installed package.
out of date Any package that can be upgraded.
stale Any package that depends on a package that is out of date.
leaf Any package that is not depended on by any other package.
Selecting the type or types of packages that are listed, and how the
information is put to use, can be controlled by the following options:
--AA This option modifies the output of --RR to use pkg_add(1) to rein-
stall deleted packages, instead of building directly from the
pkgsrc tree.
--aa All packages. This option can be specified more than once to
augment the meaning of ``all'' as it relates to the selected
output mode. For the default graph output, this option has no
effect. When used with --mm where the default output is the list
of out of date packages, one --aa will make ppkkggddeeppggrraapphh also list
stale packages, and a second --aa will make it list all installed
packages.
--CC Asserts --cc and adds ``CLEANDEPENDS=YES'' so that all rebuilt
dependencies are cleaned as well.
--cc Inserts a ``make clean'' command in the set of instructions to
rebuild packages (see the --RR option) after they are installed.
The --cc option used to enable graph coloring, but now graph col-
oring is done automatically if any coloring data is given.
--DD Instead of the standard graph output, ppkkggddeeppggrraapphh lists the
packages that need to be deleted, in ``least required first''
order, so that they can be deleted without any dependency prob-
lems. This is a good first step to take before rebuilding all
your packages that are out of date (see the --RR option). This
option is an alias for --aass.
--dd _p_k_g___d_b_d_i_r
The location of the package database files. This defaults to
the value of the _P_K_G___D_B_D_I_R environment variable, if it is set,
or to _@_P_K_G___D_B_D_I_R_@.
--ee Adds an ``existence check'' to each of the lines of sh(1) style
output so that commands can be skipped if the package is already
installed.
--FF Makes ppkkggddeeppggrraapphh emit a series of sh(1) commands that will
fetch all the required distfiles for rebuilding out of date
packages. This option is an alias for --mm _f_e_t_c_h.
--ff Force a rebuild of all dependent packages. This option is for
use in conjunction with the --UU option to force a rebuild of all
the dependencies of a package to be updated.
--gg Add package subgraph groupings. This places all packages with a
common prefix (e.g., ``p5'' or ``gimp'') in a subgraph with a
line around it so you can visually associate packages.
--ii _i_m_p_a_c_t
Specify the impact you want to allow when rebuilding packages.
The impact of a given package being rebuilt is a number that
reflects the longest path from the ``top'' of the installed
packages (those packages upon which most other packages depend)
to the ``bottom'' of the installed packages (those packages upon
which no other packages depend). A package that needs to be
rebuilt but has nothing above it in the tree requiring rebuild-
ing and nothing below it has an impact of 0. Values of 10 or
more for _i_m_p_a_c_t will probably eliminate nothing from the rebuild
list. The value for _i_m_p_a_c_t can also be the name of a package to
be avoided when constructing the list of things to be rebuilt.
--KK _p_a_c_k_a_g_e_s
The base location or url from which to install binary packages.
This defaults to the value of PKG_PATH, or to PACKAGES with
``/All'' added to the end, or to the value determined for pkgsr-
cdir (see the --PP option) with ``/packages/All'' appended to it.
--LL Limit the graph to those packages that are out of date or ulti-
mately depend on some package that is. When used with --mm or --ss,
this limits the list to ``leaf'' packages that are out of date
or stale.
--ll Adds package ``location'' information to the labels on the
nodes. This is the path to the package under the local pkgsrc
tree.
--MM _m_a_k_e Sets the name or path of the proper make(1) program for the
sh(1) commands generated by the --RR and --FF options. Some systems
may have more than one make(1) command or the correct choice
might not be named ``make''. The default value for this is
taken from the MAKE environment variable, if it is set, or is
simply ``make''.
--mm _t_a_r_g_e_t
Emits generic sh(1) commands to ``make _t_a_r_g_e_t'' for each listed
package. One interesting target to specify is _'_$_*_' since that
will yield a script that will pass its own command line argu-
ments to make for processing.
--OO _p_a_c_k_a_g_e
Marks _p_a_c_k_a_g_e as out of date so that you can see the impact of
deleting dependent packages and rebuilding everything. This
option can be used as a quick alternative to using the output
from lintpkgsrc(1) --ii for graph coloring information.
--oo Adds a package ``order'' number to each node's label. The num-
ber indicates only the place of the node in the hierarchy, such
that each node has a number greater than that of anything which
depends on it, and with the ``leaf'' nodes numbered one.
--PP _p_k_g_s_r_c_d_i_r
The root of the pkgsrc source tree. This defaults to the value
of the _P_K_G_S_R_C_D_I_R environment variable, if it is set, or to
_@_P_K_G_S_R_C_D_I_R_@.
--RR Instead of the standard graph output, emits a series of sh(1)
commands that will rebuild all the out of date packages by
rebuilding all the deleted leaf packages. Packages that are out
of date should be deleted first (see the --DD option). This
option is an alias for --eeLLmm _t_a_r_g_e_t. See the --tt option for a
description of what value is used for _t_a_r_g_e_t.
--rr Reverses the order in which the packages are listed.
--SS _p_a_c_k_a_g_e
Selects a subgraph of the installed package base around which to
generate output. By default, if a subgraph of _p_a_c_k_a_g_e is
requested, only _p_a_c_k_a_g_e and those packages immediately above and
below it in the graph will be listed. To select only those
immediately below, prefix a ``-'' to the package name. To
select all packages below _p_a_c_k_a_g_e, prefix ``--'' to the package
name. To select packages above _p_a_c_k_a_g_e, use ``+'' and ``++'' as
appropriate. To select all packages remotely connected to
_p_a_c_k_a_g_e, add ``='' to the beginning. Note: take care when using
this option with generated lists or sh scripts. Since the sub-
graph of packages selected by this option may not include all
stale or out of date packages involved in a proper package
update procedure, the lists may be incomplete and can cause
problems.
--ss Print a simple list of packages instead of sh(1) commands. The
default output set for this command is out of date packages from
the entire package set. Use combinations of --aa, --LL, --OO _p_a_c_k_a_g_e,
and --SS _p_a_c_k_a_g_e to select more or other package lists.
--tt _t_a_r_g_e_t
Changes the target of the --RR output from ``install'' to _t_a_r_g_e_t
in case you want to ``make package'' or some other target.
--UU _p_a_c_k_a_g_e
Generates a graph with only the out of date dependencies of
_p_a_c_k_a_g_e marked in red (ie, packages that are out of date but not
dependencies of _p_a_c_k_a_g_e will not be considered out of date).
This will show the entire effect of deleting all related pack-
ages in order to update the dependencies of the given package.
If the --ff option is used, then all the dependencies of _p_a_c_k_a_g_e
will be marked as out of date and the effect on the installed
package base will be accordingly greater.
--vv Adds the package version number to the node's label in the
graph.
EENNVVIIRROONNMMEENNTT
MMAAKKEE
The default name for the ``make'' program used in the sh(1) commands gen-
erated by the --FF and --RR output modes.
PPKKGG__DDBBDDIIRR
Where to find registered packages instead of _@_P_K_G___D_B_D_I_R_@.
PPKKGGSSRRCCDDIIRR
Where the pkgsrc tree is located instead of _@_P_K_G_S_R_C_D_I_R_@.
PPKKGG__PPAATTHH
PPAACCKKAAGGEESS
Two variables that can be used as a location for binary packages. See
the --AA and --KK options for more details.
EEXXAAMMPPLLEESS
The default output:
$ pkgdepgraph | dotty -
To generate graph output for later use, and a postscript image of it:
$ lintpkgsrc -i > pkgdepgraph.in
$ audit-packages >> pkgdepgraph.in
$ pkgdepgraph -lv pkgdepgraph.in > pkgdepgraph.dot
$ dot -Tps pkgdepgraph.dot > pkgdepgraph.ps
To generate a graph of just the out of date nodes as a gif:
$ pkgdepgraph -L pkgdepgraph.in > out-of-date.dot
$ dot -Tgif out-of-date.dot > out-of-date.gif
To make a list of packages that need to be removed in order to bring all
packages up to date:
$ pkgdepgraph -D pkgdepgraph.in > delete_order
$ pkgdepgraph -R pkgdepgraph.in > rebuild.sh
To rebuild everything that has no other rebuild requirements:
$ pkgdepgraph -D pkgdepgraph.in -i0 > delete_order
$ pkgdepgraph -R pkgdepgraph.in -i0 > rebuild.sh
Or, to rebuild everything except those things that would require rebuild-
ing perl:
$ pkgdepgraph -D pkgdepgraph.in -iperl > delete_order
$ pkgdepgraph -R pkgdepgraph.in -iperl > rebuild.sh
To subsequently delete all out of date and stale packages:
$ pkg_delete `cat delete_order`
$ sh rebuild.sh
You may also find it useful to ``pre-fetch'' all the required distfiles
for the rebuild as below, so that you can do all the rebuilding while
off-line:
$ pkgdepgraph -F pkgdepgraph.in > fetch.sh
$ sh fetch.sh
Running the rebuild script will probably take a while, but if you need to
stop it or it stops by itself for any reason, you can rerun the script,
and it will pick up where it left off. If there are certain packages
that you want installed before others are reinstalled, feel free to
reorder the lines in the script.
You can also ``make clean'' wherever you like when the rebuild script is
not running, or even update your tree in between runs of the script,
though in the latter case, it would be prudent to resume the script in
the following manner so that any packages that are newly out of date are
removed and replaced.
$ pkgdepgraph -D pkgdepgraph.in > delete_order
$ pkgdepgraph -R pkgdepgraph.in >> rebuild.sh
$ pkg_delete `cat delete_order`
$ sh rebuild.sh
It is important to note that if you have specifically installed a partic-
ular package that is a dependency, and another package (or other pack-
ages) exist that can satisfy the same dependency, you should probably
pre-install that particular package. There is no guarantee that the typ-
ical processing order will install the same package.
A similar task is to update all the out of date dependencies of an pack-
age, rebuild the package, and any others that might share the same depen-
dencies:
$ pkgdepgraph -U mozilla -D pkgdepgraph.in > delete_order
$ pkgdepgraph -U mozilla -R pkgdepgraph.in > rebuild.sh
$ pkg_delete `cat delete_order`
$ sh rebuild.sh
Or perhaps to rebuild a specific package and all packages which depend on
it:
$ pkgdepgraph -O glib -D > delete_order
$ pkgdepgraph -O glib -R > rebuild.sh
$ pkg_delete `cat delete_order`
$ sh rebuild.sh
Finally, to see the current state of the installed packages after some
packages have been deleted, and some (but perhaps not all) have been
reinstalled:
$ pkgdepgraph out-of-date.dot | dot -Tps > current.ps
SSEEEE AALLSSOO
dot(1), dotty(1), lintpkgsrc(1)
AAUUTTHHOORRSS
Andrew Brown <atatat@netbsd.org>
BBUUGGSS
ppkkggddeeppggrraapphh was written in perl(1), but I believe the balance of code
layout and comments is actually reasonable, despite what you may think of
perl.
The ``rebuild'' output, ``fetch'' output, and the generic ``make target''
output (see the --RR, --FF, and --mm options) do not deal very well with pack-
ages that have moved within the pkgsrc tree. Feeding the output of
lintpkgsrc(1) --gg to ppkkggddeeppggrraapphh can offset this (it contains the current
paths to all packages under _P_K_G_S_R_C_D_I_R) but sometimes is necessary that
you edit the generated scripts by hand to work around this.
NetBSD 2.0 July 28, 2004 NetBSD 2.0

View File

@@ -0,0 +1,542 @@
.\" $NetBSD: pkgdepgraph.1,v 1.12 2004/07/28 11:01:18 wiz Exp $
.\"
.\" Copyright (c) 2002, 2003, 2004 by Andrew Brown <atatat@netbsd.org>
.\" Absolutely no warranty.
.\"
.Dd July 28, 2004
.Dt PKGDEPGRAPH 1
.Sh NAME
.Nm pkgdepgraph
.Nd visual representation of installed packages
.Sh SYNOPSIS
.Nm
.Op Fl AaCcDeFfgLloRrsv
.Op Fl d Ar pkg_dbdir
.Op Fl i Ar impact
.Op Fl K Ar packages
.Op Fl M Ar make
.Op Fl m Ar target
.Op Fl O Ar package
.Op Fl P Ar pkgsrcdir
.Op Fl S Ar package
.Op Fl t Ar target
.Op Fl U Ar package
.Op Pa data ...
.Sh DESCRIPTION
.Nm
emits a
.Pa dot
language description of the locally installed packages, with the
inter-dependencies reduced to a minimal set of edges.
It scans each directory under the package database directory for files
named
.Pa +BUILD_INFO
and
.Pa +REQUIRED_BY
in order to gather its data.
.Pp
.Nm
will also read any files passed to it as arguments (or stdin if it
is not connected to a tty), in search of output that resembles that
of either
.Xr lintpkgsrc 1
.Fl i ,
.Xr lintpkgsrc 1
.Fl g ,
.Xr audit-packages 8 ,
or the output from a previous incantation of this program.
.Pp
In the latter case,
.Nm
will recolor the graph passed as input based, as usual, on information
about currently installed pkgs.
Packages that are in the input graph, but not currently installed,
will be colored black, those that are installed will be colored green,
and those that remain out of date for some reason will remain red.
.Pp
The graph defaults to black, but if data from
.Xr lintpkgsrc 1
.Fl i
or
.Pa audit-packages
is given, or if a specific package is asserted to be out of date (via the
.Fl O
option), then nodes (and edges leading from them) will be colored in
the following manner:
.Bl -tag -width purple
.It green
The package is completely up to date.
.It orange
The package is up to date, but some of its dependencies require updating.
.It red
The package is out of date or has had a security advisory issued
against it, and should be replaced.
.It purple
The package was not found in the local pkgsrc tree.
.It blue
The package depends on something not found in the local pkgsrc tree.
.It black
The package has been removed and not yet replaced, or the graph is
uncolored.
The difference between an uncolored graph and a graph that has been
recolored almost entirely black as a result of removing all the
packages can be visually detected by the color of the
.Nm
package with which the graph was recolored.
.El
.Pp
It is important to note that all installed packages fall into one (or
more) of the following categories:
.Bl -tag -width out-of-date
.It installed
Any installed package.
.It out of date
Any package that can be upgraded.
.It stale
Any package that depends on a package that is out of date.
.It leaf
Any package that is not depended on by any other package.
.El
.Pp
Selecting the type or types of packages that are listed, and how the
information is put to use, can be controlled by the following options:
.Bl -tag -width package
.It Fl A
This option modifies the output of
.Fl R
to use
.Xr pkg_add 1
to reinstall deleted packages, instead of building directly from the
pkgsrc tree.
.It Fl a
All packages.
This option can be specified more than once to augment the meaning of
.Dq all
as it relates to the selected output mode.
For the default graph output, this option has no effect.
When used with
.Fl m
where the default output is the list of out of date packages, one
.Fl a
will make
.Nm
also list stale packages, and a second
.Fl a
will make it list all installed packages.
.It Fl C
Asserts
.Fl c
and adds
.Dq CLEANDEPENDS=YES
so that all rebuilt dependencies are cleaned as well.
.It Fl c
Inserts a
.Dq make clean
command in the set of instructions to rebuild packages (see the
.Fl R
option) after they are installed.
The
.Fl c
option used to enable graph coloring, but now graph coloring is done
automatically if any coloring data is given.
.It Fl D
Instead of the standard graph output,
.Nm
lists the packages that need to be deleted, in
.Dq least required first
order, so that they can be deleted without any dependency problems.
This is a good first step to take before rebuilding all your packages
that are out of date (see the
.Fl R
option).
This option is an alias for
.Fl as .
.It Fl d Ar pkg_dbdir
The location of the package database files.
This defaults to the value of the
.Pa PKG_DBDIR
environment variable, if it is set, or to
.Pa @PKG_DBDIR@ .
.It Fl e
Adds an
.Dq existence check
to each of the lines of
.Xr sh 1
style output so that commands can be skipped if the package is already
installed.
.It Fl F
Makes
.Nm
emit a series of
.Xr sh 1
commands that will fetch all the required distfiles for rebuilding out
of date packages.
This option is an alias for
.Fl m Ar fetch .
.It Fl f
Force a rebuild of all dependent packages.
This option is for use in conjunction with the
.Fl U
option to force a rebuild of all the dependencies of a package to be
updated.
.It Fl g
Add package subgraph groupings.
This places all packages with a common prefix (e.g.,
.Dq p5
or
.Dq gimp )
in a subgraph with a line around it so you can visually associate
packages.
.It Fl i Ar impact
Specify the impact you want to allow when rebuilding packages.
The impact of a given package being rebuilt is a number that reflects
the longest path from the
.Dq top
of the installed packages (those packages upon which most other
packages depend) to the
.Dq bottom
of the installed packages (those packages upon which no other packages
depend).
A package that needs to be rebuilt but has nothing above it in the
tree requiring rebuilding and nothing below it has an impact of 0.
Values of 10 or more for
.Ar impact
will probably eliminate nothing from the rebuild list.
The value for
.Ar impact
can also be the name of a package to be avoided when constructing the
list of things to be rebuilt.
.It Fl K Ar packages
The base location or url from which to install binary packages.
This defaults to the value of PKG_PATH, or to PACKAGES with
.Dq Dv /All
added to the end, or to the value determined for pkgsrcdir (see the
.Fl P
option) with
.Dq Dv /packages/All
appended to it.
.It Fl L
Limit the graph to those packages that are out of date
or ultimately depend on some package that is.
When used with
.Fl m
or
.Fl s ,
this limits the list to
.Dq leaf
packages that are out of date or stale.
.It Fl l
Adds package
.Dq location
information to the labels on the nodes.
This is the path to the package under the local pkgsrc tree.
.It Fl M Ar make
Sets the name or path of the proper
.Xr make 1
program for the
.Xr sh 1
commands generated by the
.Fl R
and
.Fl F
options.
Some systems may have more than one
.Xr make 1
command or the correct choice might not be named
.Dq make .
The default value for this is taken from the MAKE environment
variable, if it is set, or is simply
.Dq make .
.It Fl m Ar target
Emits generic
.Xr sh 1
commands to
.Dq make Ar target
for each listed package.
One interesting target to specify is
.Ar '$*'
since that will yield a script that will pass its own command
line arguments to make for processing.
.It Fl O Ar package
Marks
.Ar package
as out of date so that you can see the impact of deleting dependent
packages and rebuilding everything.
This option can be used as a quick alternative to using the output
from
.Xr lintpkgsrc 1
.Fl i
for graph coloring information.
.It Fl o
Adds a package
.Dq order
number to each node's label.
The number indicates only the place of the node in the hierarchy, such
that each node has a number greater than that of anything which
depends on it, and with the
.Dq leaf
nodes numbered one.
.It Fl P Ar pkgsrcdir
The root of the pkgsrc source tree.
This defaults to the value of the
.Pa PKGSRCDIR
environment variable, if it is set, or to
.Pa @PKGSRCDIR@ .
.It Fl R
Instead of the standard graph output, emits a series of
.Xr sh 1
commands that will rebuild all the out of date packages by rebuilding
all the deleted leaf packages.
Packages that are out of date should be deleted first (see the
.Fl D
option).
This option is an alias for
.Fl eLm Ar target .
See the
.Fl t
option for a description of what value is used for
.Ar target .
.It Fl r
Reverses the order in which the packages are listed.
.It Fl S Ar package
Selects a subgraph of the installed package base around which to
generate output.
By default, if a subgraph of
.Ar package
is requested, only
.Ar package
and those packages immediately above and below it in the graph will be
listed.
To select only those immediately below, prefix a
.Dq -
to the package name.
To select all packages below
.Ar package ,
prefix
.Dq --
to the package name.
To select packages above
.Ar package ,
use
.Dq +
and
.Dq ++
as appropriate.
To select all packages remotely connected to
.Ar package ,
add
.Dq =
to the beginning.
Note: take care when using this option with generated lists or sh
scripts.
Since the subgraph of packages selected by this option may not include
all stale or out of date packages involved in a proper package update
procedure, the lists may be incomplete and can cause problems.
.It Fl s
Print a simple list of packages instead of
.Xr sh 1
commands.
The default output set for this command is out of date packages from
the entire package set.
Use combinations of
.Fl a ,
.Fl L ,
.Fl O Ar package ,
and
.Fl S Ar package
to select more or other package lists.
.It Fl t Ar target
Changes the target of the
.Fl R
output from
.Dq install
to
.Ar target
in case you want to
.Dq make package
or some other target.
.It Fl U Ar package
Generates a graph with only the out of date dependencies of
.Ar package
marked in red (ie, packages that are out of date but not dependencies
of
.Ar package
will not be considered out of date).
This will show the entire effect of deleting all related packages in
order to update the dependencies of the given package.
If the
.Fl f
option is used, then all the dependencies of
.Ar package
will be marked as out of date and the effect on the installed package
base will be accordingly greater.
.It Fl v
Adds the package version number to the node's label in the graph.
.El
.Sh ENVIRONMENT
.Ss MAKE
The default name for the
.Dq make
program used in the
.Xr sh 1
commands generated by the
.Fl F
and
.Fl R
output modes.
.Ss PKG_DBDIR
Where to find registered packages instead of
.Pa @PKG_DBDIR@ .
.Ss PKGSRCDIR
Where the pkgsrc tree is located instead of
.Pa @PKGSRCDIR@ .
.Ss PKG_PATH
.Ss PACKAGES
Two variables that can be used as a location for binary packages.
See the
.Fl A
and
.Fl K
options for more details.
.Sh EXAMPLES
The default output:
.Bd -literal -offset indent
$ pkgdepgraph | dotty -
.Ed
.Pp
To generate graph output for later use, and a postscript image of it:
.Bd -literal -offset indent
$ lintpkgsrc -i \*[Gt] pkgdepgraph.in
$ audit-packages \*[Gt]\*[Gt] pkgdepgraph.in
$ pkgdepgraph -lv pkgdepgraph.in \*[Gt] pkgdepgraph.dot
$ dot -Tps pkgdepgraph.dot \*[Gt] pkgdepgraph.ps
.Ed
.Pp
To generate a graph of just the out of date nodes as a gif:
.Bd -literal -offset indent
$ pkgdepgraph -L pkgdepgraph.in \*[Gt] out-of-date.dot
$ dot -Tgif out-of-date.dot \*[Gt] out-of-date.gif
.Ed
.Pp
To make a list of packages that need to be removed in order to bring
all packages up to date:
.Bd -literal -offset indent
$ pkgdepgraph -D pkgdepgraph.in \*[Gt] delete_order
$ pkgdepgraph -R pkgdepgraph.in \*[Gt] rebuild.sh
.Ed
.Pp
To rebuild everything that has no other rebuild requirements:
.Bd -literal -offset indent
$ pkgdepgraph -D pkgdepgraph.in -i0 \*[Gt] delete_order
$ pkgdepgraph -R pkgdepgraph.in -i0 \*[Gt] rebuild.sh
.Ed
.Pp
Or, to rebuild everything except those things that would require
rebuilding perl:
.Bd -literal -offset indent
$ pkgdepgraph -D pkgdepgraph.in -iperl \*[Gt] delete_order
$ pkgdepgraph -R pkgdepgraph.in -iperl \*[Gt] rebuild.sh
.Ed
.Pp
To subsequently delete all out of date and stale packages:
.Bd -literal -offset indent
$ pkg_delete `cat delete_order`
$ sh rebuild.sh
.Ed
.Pp
You may also find it useful to
.Dq pre-fetch
all the required distfiles for the rebuild as below, so that you can
do all the rebuilding while off-line:
.Bd -literal -offset indent
$ pkgdepgraph -F pkgdepgraph.in \*[Gt] fetch.sh
$ sh fetch.sh
.Ed
.Pp
Running the rebuild script will probably take a while, but if you
need to stop it or it stops by itself for any reason, you can rerun
the script, and it will pick up where it left off.
If there are certain packages that you want installed before others
are reinstalled, feel free to reorder the lines in the script.
.Pp
You can also
.Dq make clean
wherever you like when the rebuild script is not running, or even
update your tree in between runs of the script, though in the latter
case, it would be prudent to resume the script in the following manner
so that any packages that are newly out of date are removed and
replaced.
.Bd -literal -offset indent
$ pkgdepgraph -D pkgdepgraph.in \*[Gt] delete_order
$ pkgdepgraph -R pkgdepgraph.in \*[Gt]\*[Gt] rebuild.sh
$ pkg_delete `cat delete_order`
$ sh rebuild.sh
.Ed
.Pp
It is important to note that if you have specifically installed a
particular package that is a dependency, and another package (or other
packages) exist that can satisfy the same dependency, you should
probably pre-install that particular package.
There is no guarantee that the typical processing order will install
the same package.
.Pp
A similar task is to update all the out of date dependencies of an
package, rebuild the package, and any others that might share the same
dependencies:
.Bd -literal -offset indent
$ pkgdepgraph -U mozilla -D pkgdepgraph.in \*[Gt] delete_order
$ pkgdepgraph -U mozilla -R pkgdepgraph.in \*[Gt] rebuild.sh
$ pkg_delete `cat delete_order`
$ sh rebuild.sh
.Ed
.Pp
Or perhaps to rebuild a specific package and all packages which depend
on it:
.Bd -literal -offset indent
$ pkgdepgraph -O glib -D \*[Gt] delete_order
$ pkgdepgraph -O glib -R \*[Gt] rebuild.sh
$ pkg_delete `cat delete_order`
$ sh rebuild.sh
.Ed
.Pp
Finally, to see the current state of the installed packages after some
packages have been deleted, and some (but perhaps not all) have been
reinstalled:
.Bd -literal -offset indent
$ pkgdepgraph out-of-date.dot | dot -Tps \*[Gt] current.ps
.Ed
.Sh SEE ALSO
.Xr dot 1 ,
.Xr dotty 1 ,
.Xr lintpkgsrc 1
.Sh AUTHORS
.An Andrew Brown Aq atatat@netbsd.org
.Sh BUGS
.Nm
was written in
.Xr perl 1 ,
but I believe the balance of code layout and comments is actually
reasonable, despite what you may think of perl.
.Pp
The
.Dq rebuild
output,
.Dq fetch
output, and the generic
.Dq make target
output (see the
.Fl R ,
.Fl F ,
and
.Fl m
options) do not deal very well with packages that have moved within
the pkgsrc tree.
Feeding the output of
.Xr lintpkgsrc 1
.Fl g
to
.Nm
can offset this (it contains the current paths to all packages under
.Pa PKGSRCDIR )
but sometimes is necessary that you edit the generated scripts by hand
to work around this.

View File

@@ -0,0 +1,737 @@
#!@PREFIX@/bin/perl
# Copyright (c) 2002, 2003, 2004 by Andrew Brown <atatat@netbsd.org>
# Absolutely no warranty.
# $NetBSD: pkgdepgraph.pl,v 1.11 2005/06/26 17:34:19 atatat Exp $
# pkgdepgraph: @DISTVER@
use strict;
# no strict 'refs';
use Getopt::Long;
Getopt::Long::Configure("bundling");
my(@opts, %opt);
my($iam, $version, $usecolor, $group, $locations, $order, $versions);
my($limit, $delete, $rebuild, $force, @outofdate, @update, $clean);
my($pkg_dbdir, $pkgsrcdir, $packages, $pkgadd, $fetch, $make);
my($all, $target, $exists, $reverse, $simple, @subgraph, @impact, %impactof);
$version = '@DISTVER@';
($iam = $0) =~ s:.*/::;
@opts = ('A', 'a+', 'C', 'c', 'D', 'd=s', 'e', 'F', 'f', 'g', 'i=s',
'K=s', 'L', 'l', 'M=s', 'm=s', 'O=s', 'o', 'P=s', 'R', 'r',
'S=s', 's', 't=s', 'U=s', 'v');
%opt = (
'A' => \$pkgadd,
'a' => \$all,
# 'C' => implies "realclean", handled later
# 'c' => implies "clean", handled later
'D' => \$delete,
'd' => \$pkg_dbdir,
'e' => \$exists,
'F' => \$fetch,
'f' => \$force,
'g' => \$group,
'i' => \@impact,
'K' => \$packages,
'L' => \$limit,
'l' => \$locations,
'M' => \$make,
'm' => \$target,
'O' => \@outofdate,
'o' => \$order,
'P' => \$pkgsrcdir,
'R' => \$rebuild,
'r' => \$reverse,
'S' => \@subgraph,
's' => \$simple,
# 't' => goes to rebuild, handled later
'U' => \@update,
'v' => \$versions,
);
die("usage: $iam [-AaCcDeFfgLloRrsv] [-d pkg_dbdir] [-i impact]\n",
" " x (length($iam) + 8),
"[-K packages] [-M make] [-m target] [-O package]\n",
" " x (length($iam) + 8),
"[-P pkgsrcdir] [-S package] [-t target] [-U package]\n",
" " x (length($iam) + 8),
"[data ...]\n")
if (!GetOptions(\%opt, @opts));
die("$iam: -D, -F, -m, and -R are mutually exclusive -- please pick one\n")
if (($delete != 0) +
($fetch != 0) +
($target ne "") +
($rebuild ne "") > 1);
$pkg_dbdir ||= $ENV{'PKG_DBDIR'} || "@PKG_DBDIR@";
$pkgsrcdir ||= $ENV{'PKGSRCDIR'} || "@PKGSRCDIR@";
$packages = $ENV{'PKG_PATH'} if (!$packages);
$packages = $ENV{'PACKAGES'} . "/All" if (!$packages && $ENV{'PACKAGES'});
$packages = $pkgsrcdir . "/packages/All" if (!$packages);
$rebuild &&= $opt{t} || "install";
$clean = "clean" if ($opt{c});
$clean = "CLEANDEPENDS=YES clean" if ($opt{C});
$make ||= $ENV{'MAKE'} || "make";
my(@pkgs, $pkg, $req, %req, %dep, @reqs, @rreqs);
my(%clusters, $cluster);
my(%where, $pkgcnt, $num, %num, @num, %ord, $suffix);
my(%color, $color, %vuln);
my(%need, %forced, $label);
my($recolor, @graph);
my(%vpkgs);
# @pkgs - list of all installed pkgs
# %req - pkg to ref to hash of pkgs that it requires
# %dep - pkg to ref to hash of pkgs that depend on it
# %clusters - pkg prefix to number of pkgs that share the prefix
# %where - pkg to location in source tree
# %num/@num - pkg to group number/group number array ref
# %ord - pkg to its height in the tree
# %color - pkg to pkg color (green, yellow, red, etc)
# %vuln - pkg to vulnerabilities recorded against it
# %need - pkg to version required (pkg is out of date)
# %forced - pkg marked as "forced" to be out of date
# %vpkgs - pkg is viewable (part of selected subgraph)
##
## load out-of-date or security problem list (if given), or a graph to
## recolor
##
$recolor = 0;
if (@ARGV || ! -t) {
$usecolor = 1;
while (<>) {
if (/^digraph/) {
$recolor = 1;
@graph = ($_);
}
elsif ($recolor > 0) {
push(@graph, $_);
$recolor++ if (/^subgraph/);
$recolor-- if (/^\}/);
$recolor -= ($recolor == 0);
}
elsif (m:^([^/\s]+)\t([^/\s]+/[^/\s]+)\t(\d+[^/\s]*)$:) {
$where{"$1-$3"} = $2;
}
elsif (/^Version mismatch: '(\S+)' (\S+) vs (\S+)/) {
$color{"$1-$2"} = "red";
$need{"$1-$2"} = "$1-$3";
}
elsif (/^Unknown package: '(\S+)' version (\S+)/) {
$color{"$1-$2"} = "purple";
}
elsif (/Package (\S+) has a (\S+) vulnerability/) {
$vuln{$1} = $2;
$color{$1} = "red";
}
}
}
##
## load pkg list
##
opendir(P, $pkg_dbdir) || die("opendir");
@pkgs = grep(/-/ && -d "$pkg_dbdir/$_" && -f "$pkg_dbdir/$_/+BUILD_INFO",
readdir(P));
closedir(P);
$pkgcnt = @pkgs;
##
## where are they needed
##
foreach $pkg (@pkgs) {
$where{$pkg} ||= $pkg;
open(R, "<$pkg_dbdir/$pkg/+BUILD_INFO") ||
die("$pkg: +BUILD_INFO: $!\n");
while (<R>) {
if (/^PKGPATH\s*=\s*(\S+)/) {
$where{$pkg} = $1 if ($where{$pkg} eq $pkg);
last;
}
}
close(R);
next if (!open(R, "<$pkg_dbdir/$pkg/+REQUIRED_BY"));
while ($req = <R>) {
chomp($req);
$req{$req}->{$pkg} = 1;
$dep{$pkg}->{$req} = 1;
}
close(R);
}
##
## reset %where based on "better" information, if we have it
##
foreach $pkg (@pkgs) {
if ($need{$pkg} && $where{$need{$pkg}}) {
$where{$pkg} = $where{$need{$pkg}};
}
}
##
## if we're recoloring an existing graph, recolor it now and finish
##
if ($recolor) {
my(%over, %nver, @label, $ocolor);
map({ /(.*)-(.*)/ && ($nver{$1} = $2) } @pkgs);
foreach (@graph) {
# we don't recolor edges
($pkg) = (/\"([^\"]+)\"/);
$pkg =~ s/(.*)-(.*)/$1/;
$over{$pkg} = $2;
if (/, EDGE$/) {
if (defined($nver{$pkg})) {
s/color=\"[^\"]+\"/color=\"green\"/;
}
else {
s/color=\"[^\"]+\"/color=\"black\"/;
}
}
elsif (/label=/) {
s/color=\"([^\"]+)\"/color="NEWCOLOR"/;
$ocolor = $1;
s/label=\"([^\"]+)\"/label="NEWLABEL"/;
$label = $1;
if ($nver{$pkg}) {
if ($nver{$pkg} ne $over{$pkg} || $ocolor ne "red") {
s/NEWCOLOR/green/;
}
else {
s/NEWCOLOR/$ocolor/;
}
@label = split(/\\n/, $label);
$label = "";
# "where" tag
if ($label[0] =~ m:/:) {
$label .= "\\n" . shift(@label);
}
# installed pkg
$label[0] =~ s/(.*$pkg)-\S*$/$1-$nver{$pkg}/ if ($nver{$pkg});
$label .= "\\n" . shift(@label);
# "needed" pkg
if ($label[0] =~ /^$pkg-(.*)/) {
$label .= "\\n$label[0]" if ($1 ne $nver{$pkg});
shift(@label);
}
# there shouldn't be anything left, but...
$label .= "\\n" . join("\\n", @label);
$label =~ s/\\n//;
}
else {
s/NEWCOLOR/black/;
}
s/NEWLABEL/$label/;
}
print;
}
exit(0);
}
##
## eliminate redundancies by deleting edges that are redundant
##
foreach $pkg (@pkgs) {
@reqs = sort(keys %{$req{$pkg}});
@rreqs = recurse(\%req, @reqs);
map(delete($req{$pkg}->{$_}), @rreqs);
@reqs = sort(keys %{$dep{$pkg}});
@rreqs = recurse(\%dep, @reqs);
map(delete($dep{$pkg}->{$_}), @rreqs);
}
##
## create a hash of clusters of package prefixes, with counts. later,
## clusters that have more than one member can be marked as subgraphs.
##
## the outer map() iterates over each pkg name after all instances of
## _ in the pkg name have been changed to - (for the purposes of
## accurate clustering). the inner map() breaks each pkg name up into
## tokens that end in - and loops over the resulting list, appending
## each one to $a. for example:
##
## pkg: one_two-three-4.56
## tokens: one- two- three-
## $a: one- one-two- one-two-three-
##
map({ $a = "";
($b = $_) =~ s/_/-/g;
map({ $a .= $_; $clusters{$a}++; }
$b =~ /([^-]*-)/g); }
@pkgs);
##
## impose some sort of order on the pkgs by assigning them numbers
## that indicate their height in the graph. leaf pkgs will always
## have an order of 1, and each pkg above will be numbered at least 2
## (possibly higher, if there exists another longer path to another
## leaf).
##
map(order(1, $_), @pkgs);
##
## assign each pkg a group number, and count the number of pkgs in
## that group. the group numbers are arbitrary, and serve only to
## identify pkgs that belong to the same group.
##
$num = 1;
foreach $pkg (@pkgs) {
my($pkgnum);
# my direct requirements
@reqs = sort(keys %{$req{$pkg}});
# all the requirements of my requirements
@rreqs = recurse(\%req, @reqs);
# the lowest group number from all of those
$pkgnum = number($pkg, @reqs, @rreqs) || $num;
# stuff all those into the list for that group
push(@{$num[$pkgnum]}, $pkg, @reqs, @rreqs);
# now check for packages coming from other groups
foreach $req ($pkg, @reqs, @rreqs) {
# no group yet, skip on
next if (!$num{$req});
# was $req in a different group
if ($num{$req} != $pkgnum) {
# yes, pull that group into the current group
push(@{$num[$pkgnum]}, @{$num[$num{$req}]});
# empty out the old group
@{$num[$num{$req}]} = ();
}
}
# reduce the group list
@{$num[$pkgnum]} = uniq(sort(@{$num[$pkgnum]}));
# make sure all packages in this group know
map($num{$_} = $pkgnum, @{$num[$pkgnum]});
# skip to next available group number
$num += ($num == $pkgnum);
}
##
## if we want to check a specific pkg for rebuild impact, mark it as
## "forced" to be out of date, unless it already *is* out of date.
##
if (@outofdate) {
$usecolor = 1;
canonicalize(@outofdate);
foreach (@outofdate) {
if ($color{$_} ne "red") {
$color{$_} = "red";
$need{$_} = $_;
$forced{$_} = " (forced)";
}
}
}
##
## if we want to update a specific package, mark all non-related
## packages as "green". this avoids rebuilding unnecessary pkgs that
## don't depend on any of the same dependencies as the given pkg. if
## $force is set, mark *all* dependencies of the given pkg as out of
## date.
##
if (@update) {
my(@leftover);
canonicalize(@update);
@update = uniq(sort(@update, recurse(\%req, @update)));
if ($force) {
foreach (@update) {
if ($color{$_} ne "red") {
$color{$_} = "red";
$need{$_} = $_;
$forced{$_} = " (forced)";
}
}
}
foreach (sort(@pkgs)) {
if ($_ eq $update[0]) {
shift(@update);
}
else {
push(@leftover, $_);
}
}
delete(@color{@leftover});
delete(@need{@leftover});
}
##
## pick packages for a subgraph
##
## + means up from given package, - means down, ++ means all the way
## up, -- means all the way down, = means all "connected" packages, etc.
##
if (@subgraph) {
my ($sub, $up, $down, $eq);
foreach (@subgraph) {
($sub) = (/^([-+=]+)/);
s/^[-+=]+//;
$sub = "+-" if ($sub eq "");
canonicalize($_);
$up = join("", ($sub =~ /(\+)/g));
$down = join("", ($sub =~ /(-)/g));
$eq = join("", ($sub =~ /(=)/g));
if ($eq) {
map($vpkgs{$_} = 1, @{$num[$num{$_}]});
}
else {
if ($up) {
@reqs = sort(keys %{$req{$_}});
@rreqs = (length($up) > 1) ? recurse(\%req, @reqs) : ();
map($vpkgs{$_} = 1, ($_, @reqs, @rreqs));
}
if ($down) {
@reqs = sort(keys %{$dep{$_}});
@rreqs = (length($down) > 1) ? recurse(\%dep, @reqs) : ();
map($vpkgs{$_} = 1, ($_, @reqs, @rreqs));
}
}
}
}
else {
@vpkgs{@pkgs} = (1) x @pkgs;
}
##
## if checking for rebuild impact, also mark packages that are too
## deeply involved as "green" so that they're not candidates for
## destruction
##
if (@impact) {
my ($impact);
# step 1: canonicalize anything that's not a number (ie, is the
# name of a pkg) and eliminate duplicates (we just don't need 'em)
foreach (@impact) {
next if (/^\d+$/);
canonicalize($_);
}
@impact = uniq(sort(@impact));
# step 2: the "default" impact allows for anything to be rebuilt,
# but numeric values in @impact are also allowed, so pick the
# lowest one (specifying both 1 and 2 really means just 1)
$impact = $ord{(sort(byord @pkgs))[0]};
while ($impact[0] =~ /^\d+$/) {
$_ = shift(@impact);
$impact = $_ if ($_ < $impact);
}
# step 3: anything that would have too great an impact on the tree
# gets marked (the impactof() function will check the @impact
# array to avoid specific pkgs being rebuild)
foreach $pkg (keys %vpkgs) {
next if (impactof($impact, $pkg) <= $impact);
$vpkgs{$pkg} = 2;
}
# step 4: anything so marked gets tagged as green. this tagging
# is a separate step so that we can properly judge impact over the
# entire tree (marking too early could prematurely split chunks
# that need to be rebuilt)
foreach $pkg (keys %vpkgs) {
$color{$pkg} = "green" if ($vpkgs{$pkg} == 2);
}
}
##
## translate "older" alternate output modes to the new generic version
##
if ($fetch) {
$target = "fetch";
}
elsif ($rebuild) {
$exists = 1;
$limit = 1;
$target = $rebuild;
}
elsif ($delete) {
$all++;
$simple = 1;
}
##
## "target" output mode, ordered by ascendency
##
if ($target || $simple) {
my(@targets);
printf("PKG_PATH=\"$packages\"\nexport PKG_PATH\n")
if ($pkgadd && $rebuild);
@targets = grep((color($_) eq "red" && !$limit) ||
(color($_) ne "green" &&
($all || ($ord{$_} == 1 && $limit))) ||
($all > 1), keys %vpkgs);
@targets = sort(byord @targets);
@targets = reverse(@targets) if (!$reverse);
print_package(@targets);
print("true\n") if (!$simple);
exit(0);
}
##
## show left overs as a graph
##
printf("digraph \"%s packages\" {\n",
$limit ? "out of date" : "installed");
printf("label = \"%s packages %s, generated by %s v%s, on %s\";\n",
$limit ? "out of date" : "installed",
@subgraph ? "subgraph (@subgraph)" : "graph",
$iam, $version, scalar(localtime));
foreach $pkg (sort(bynum keys %vpkgs)) {
$color = color($pkg);
next if ($limit && $color eq "green");
$label = $pkg;
$label =~ s/(.*)-.*/$1/ if (!$versions);
$label = "($ord{$pkg}) $label" if ($order);
$label = "$where{$pkg}\\n$label" if ($locations);
$label .= "\\n$need{$pkg}$forced{$pkg}" if ($need{$pkg});
if ($vuln{$pkg}) {
$label .= "\\n(no update available)" if (!$need{$pkg});
$label .= "\\n[$vuln{$pkg}]";
}
$suffix = "\t// \#$ord{$pkg}, group $num{$pkg}, " .
(exists($impactof{$pkg}) ? "impact $impactof{$pkg}, " : "") .
scalar(@{$num[$num{$pkg}]}) . " members, $pkgcnt pkgs";
$suffix .= ", LEAF" if ($ord{$pkg} == 1);
##
## scan the cluster list, but in the opposite order so in the case
## of pkgs with a common "multi-token" prefix, we only emit the
## one with the longest name. we have to prepend the names to a
## buffer so that they end up being printed in the reverse of
## discovery order, so that we end up with the "least-specific"
## subgroup announced first.
##
($a = $pkg) =~ s/_/-/g;
$b = 1;
$cluster = "";
while ($group && $a =~ s/-[^-]+-?$/-/) {
next if ($clusters{$a} == $b);
$b = $clusters{$a};
$cluster = sprintf("subgraph \"cluster_%s\" {\n", substr($a, 0, -1)) .
sprintf("label = \"%s (%d)\";\n", substr($a, 0, -1), $b) .
$cluster;
}
print($cluster);
printf("\"%s\" [color=\"%s\",label=\"%s\"];$suffix\n", $pkg,
$usecolor ? $color : "black", $label);
$cluster =~ s/label = .*\n//g;
$cluster =~ s/.+\{/\}/g;
print($cluster);
@reqs = sort(keys %{$req{$pkg}});
$suffix =~ s/, LEAF$//;
$suffix .= ", EDGE";
foreach $req (@reqs) {
$color = color($req);
next if ($limit && $color eq "green");
printf("\"%s\" -> \"%s\" [color=\"%s\"];$suffix\n", $req, $pkg,
$usecolor ? $color : "black");
}
}
print("}\n");
##
## print sh(1) style commands to handle work on a given package, or
## just the package name if $simple is set
##
sub print_package {
foreach (@_) {
printf("( pkg_info -qe %s || ", /(.*)-.*/) if ($exists && !$simple);
if ($simple) {
print($_);
}
elsif ($pkgadd) {
printf("( pkg_add %s.tgz", ($need{$_} || $_));
}
else {
print("( cd $pkgsrcdir/$where{$_} && $make $target");
print(" && $make $clean") if ($clean);
}
if (!$simple) {
print(" )") if ($exists);
print(" ) &&");
}
print("\n");
}
}
##
## find all dependencies above or below a given node
##
sub recurse {
my(@list, @new, $map);
@list = ();
$map = shift;
foreach (@_) {
@new = keys %{$map->{$_}};
push(@list, @new, recurse($map, @new));
}
uniq(sort(@list));
}
##
## canonicalize a pkg name based on what we have installed
##
sub canonicalize {
my($canon, $pkg);
foreach $pkg (@_) {
# attempt to find actual pkg, first by argument given...
($canon) = grep($pkg eq $_, @pkgs);
# ...then by comparing against the internal list sans version numbers
($canon) = grep(($a = $_) =~ s/(.*)-.*/$1/ && $pkg eq $a, @pkgs)
if (!defined($canon));
die("package '$pkg' not found\n")
if (!defined($canon));
$pkg = $canon;
}
@_;
}
##
## lowest group number of a set of packages
##
sub number {
my($n, $pkg);
$n = 0;
foreach $pkg (@_) {
$n = $num{$pkg} if ($n == 0 || $num{$pkg} < $n);
}
$n + 0;
}
##
## pick a color based on the color of the dependencies
##
sub color {
my($pkg) = @_;
if ($color{$pkg}) {
$color{$pkg};
}
else {
my($req, @reqs, $color);
@reqs = sort(keys %{$req{$pkg}});
@reqs = (@reqs, recurse(\%req, @reqs));
$color = "green";
foreach $req (@reqs) {
if ($color{$req} eq "red") {
return "orange";
}
elsif ($color{$req} eq "purple") {
$color = "blue";
}
}
$color;
}
}
##
## bynum - higher numbers come last
##
sub bynum {
return $num{$a} <=> $num{$b} ||
$a cmp $b;
}
##
## byord - higher orders come first
##
sub byord {
return $ord{$b} <=> $ord{$a} ||
$b cmp $a;
}
##
## order - the order of a pkg is one higher than anything below it
##
sub order {
my($n, @pkgs) = @_;
my($pkg);
foreach $pkg (@pkgs) {
$ord{$pkg} = $n if ($ord{$pkg} <= $n);
order($n + 1, sort(keys %{$req{$pkg}}));
}
}
##
## uniq - eliminate adjacent duplicate entries in an array
##
sub uniq {
my($i);
for ($i = 0; $i < $#_; ) {
if ($_[$i] eq $_[$i + 1]) {
splice(@_, $i, 1);
}
else {
$i++;
}
}
@_;
}
##
## impactof - impact of pkg delete/rebuild is the longest path (either
## up or down the tree) that encompasses all things that need
## rebuilding
##
sub impactof {
my ($impact, $pkg) = @_;
my (@in, @out);
# if we already know or it's dead-simple, get out early
return $impactof{$pkg} if (exists($impactof{$pkg}));
return $impactof{$pkg} = 0 if (color($pkg) eq "green");
# starting with the given pkg, repeatedly look up and down the
# tree for connected pkgs that also require a rebuild
@out = ($pkg);
do {
@in = @out;
@out = ($pkg);
push(@out, grep(color($_) ne "green", recurse(\%dep, @in)));
push(@out, grep(color($_) ne "green", recurse(\%req, @out)));
@out = uniq(sort(byord @out));
} while (@in != @out);
# check to see if the set of related pkgs intersects with the set
# we want to avoid and if so, mark this set as "too expensive"
$a = "";
if (@impact) {
foreach $b (@impact) {
if (grep($_ eq $b, @out)) {
$a = $b;
$impact++;
last;
}
}
}
# if we didn't hit anything, the impact is the one less than the
# highest ordered remotely connected pkg we found (the longest
# path from the top to the bottom of the set to be rebuilt)
$impact = $ord{$out[0]} - 1 if ($a eq "");
@impactof{@in} = ($impact) x @in;
$impactof{$pkg};
}