| @@ -1,737 +1,737 @@ | | | @@ -1,737 +1,737 @@ |
1 | #!@PREFIX@/bin/perl | | 1 | #!@PERL5@ |
2 | | | 2 | |
3 | # Copyright (c) 2002, 2003, 2004 by Andrew Brown <atatat@netbsd.org> | | 3 | # Copyright (c) 2002, 2003, 2004 by Andrew Brown <atatat@netbsd.org> |
4 | # Absolutely no warranty. | | 4 | # Absolutely no warranty. |
5 | | | 5 | |
6 | # $NetBSD: pkgdepgraph.pl,v 1.11 2005/06/26 17:34:19 atatat Exp $ | | 6 | # $NetBSD: pkgdepgraph.pl,v 1.12 2014/03/03 05:06:43 obache Exp $ |
7 | # pkgdepgraph: @DISTVER@ | | 7 | # pkgdepgraph: @DISTVER@ |
8 | | | 8 | |
9 | use strict; | | 9 | use strict; |
10 | # no strict 'refs'; | | 10 | # no strict 'refs'; |
11 | | | 11 | |
12 | use Getopt::Long; | | 12 | use Getopt::Long; |
13 | Getopt::Long::Configure("bundling"); | | 13 | Getopt::Long::Configure("bundling"); |
14 | my(@opts, %opt); | | 14 | my(@opts, %opt); |
15 | my($iam, $version, $usecolor, $group, $locations, $order, $versions); | | 15 | my($iam, $version, $usecolor, $group, $locations, $order, $versions); |
16 | my($limit, $delete, $rebuild, $force, @outofdate, @update, $clean); | | 16 | my($limit, $delete, $rebuild, $force, @outofdate, @update, $clean); |
17 | my($pkg_dbdir, $pkgsrcdir, $packages, $pkgadd, $fetch, $make); | | 17 | my($pkg_dbdir, $pkgsrcdir, $packages, $pkgadd, $fetch, $make); |
18 | my($all, $target, $exists, $reverse, $simple, @subgraph, @impact, %impactof); | | 18 | my($all, $target, $exists, $reverse, $simple, @subgraph, @impact, %impactof); |
19 | | | 19 | |
20 | $version = '@DISTVER@'; | | 20 | $version = '@DISTVER@'; |
21 | ($iam = $0) =~ s:.*/::; | | 21 | ($iam = $0) =~ s:.*/::; |
22 | @opts = ('A', 'a+', 'C', 'c', 'D', 'd=s', 'e', 'F', 'f', 'g', 'i=s', | | 22 | @opts = ('A', 'a+', 'C', 'c', 'D', 'd=s', 'e', 'F', 'f', 'g', 'i=s', |
23 | 'K=s', 'L', 'l', 'M=s', 'm=s', 'O=s', 'o', 'P=s', 'R', 'r', | | 23 | 'K=s', 'L', 'l', 'M=s', 'm=s', 'O=s', 'o', 'P=s', 'R', 'r', |
24 | 'S=s', 's', 't=s', 'U=s', 'v'); | | 24 | 'S=s', 's', 't=s', 'U=s', 'v'); |
25 | | | 25 | |
26 | %opt = ( | | 26 | %opt = ( |
27 | 'A' => \$pkgadd, | | 27 | 'A' => \$pkgadd, |
28 | 'a' => \$all, | | 28 | 'a' => \$all, |
29 | # 'C' => implies "realclean", handled later | | 29 | # 'C' => implies "realclean", handled later |
30 | # 'c' => implies "clean", handled later | | 30 | # 'c' => implies "clean", handled later |
31 | 'D' => \$delete, | | 31 | 'D' => \$delete, |
32 | 'd' => \$pkg_dbdir, | | 32 | 'd' => \$pkg_dbdir, |
33 | 'e' => \$exists, | | 33 | 'e' => \$exists, |
34 | 'F' => \$fetch, | | 34 | 'F' => \$fetch, |
35 | 'f' => \$force, | | 35 | 'f' => \$force, |
36 | 'g' => \$group, | | 36 | 'g' => \$group, |
37 | 'i' => \@impact, | | 37 | 'i' => \@impact, |
38 | 'K' => \$packages, | | 38 | 'K' => \$packages, |
39 | 'L' => \$limit, | | 39 | 'L' => \$limit, |
40 | 'l' => \$locations, | | 40 | 'l' => \$locations, |
41 | 'M' => \$make, | | 41 | 'M' => \$make, |
42 | 'm' => \$target, | | 42 | 'm' => \$target, |
43 | 'O' => \@outofdate, | | 43 | 'O' => \@outofdate, |
44 | 'o' => \$order, | | 44 | 'o' => \$order, |
45 | 'P' => \$pkgsrcdir, | | 45 | 'P' => \$pkgsrcdir, |
46 | 'R' => \$rebuild, | | 46 | 'R' => \$rebuild, |
47 | 'r' => \$reverse, | | 47 | 'r' => \$reverse, |
48 | 'S' => \@subgraph, | | 48 | 'S' => \@subgraph, |
49 | 's' => \$simple, | | 49 | 's' => \$simple, |
50 | # 't' => goes to rebuild, handled later | | 50 | # 't' => goes to rebuild, handled later |
51 | 'U' => \@update, | | 51 | 'U' => \@update, |
52 | 'v' => \$versions, | | 52 | 'v' => \$versions, |
53 | ); | | 53 | ); |
54 | die("usage: $iam [-AaCcDeFfgLloRrsv] [-d pkg_dbdir] [-i impact]\n", | | 54 | die("usage: $iam [-AaCcDeFfgLloRrsv] [-d pkg_dbdir] [-i impact]\n", |
55 | " " x (length($iam) + 8), | | 55 | " " x (length($iam) + 8), |
56 | "[-K packages] [-M make] [-m target] [-O package]\n", | | 56 | "[-K packages] [-M make] [-m target] [-O package]\n", |
57 | " " x (length($iam) + 8), | | 57 | " " x (length($iam) + 8), |
58 | "[-P pkgsrcdir] [-S package] [-t target] [-U package]\n", | | 58 | "[-P pkgsrcdir] [-S package] [-t target] [-U package]\n", |
59 | " " x (length($iam) + 8), | | 59 | " " x (length($iam) + 8), |
60 | "[data ...]\n") | | 60 | "[data ...]\n") |
61 | if (!GetOptions(\%opt, @opts)); | | 61 | if (!GetOptions(\%opt, @opts)); |
62 | | | 62 | |
63 | die("$iam: -D, -F, -m, and -R are mutually exclusive -- please pick one\n") | | 63 | die("$iam: -D, -F, -m, and -R are mutually exclusive -- please pick one\n") |
64 | if (($delete != 0) + | | 64 | if (($delete != 0) + |
65 | ($fetch != 0) + | | 65 | ($fetch != 0) + |
66 | ($target ne "") + | | 66 | ($target ne "") + |
67 | ($rebuild ne "") > 1); | | 67 | ($rebuild ne "") > 1); |
68 | | | 68 | |
69 | $pkg_dbdir ||= $ENV{'PKG_DBDIR'} || "@PKG_DBDIR@"; | | 69 | $pkg_dbdir ||= $ENV{'PKG_DBDIR'} || "@PKG_DBDIR@"; |
70 | $pkgsrcdir ||= $ENV{'PKGSRCDIR'} || "@PKGSRCDIR@"; | | 70 | $pkgsrcdir ||= $ENV{'PKGSRCDIR'} || "@PKGSRCDIR@"; |
71 | $packages = $ENV{'PKG_PATH'} if (!$packages); | | 71 | $packages = $ENV{'PKG_PATH'} if (!$packages); |
72 | $packages = $ENV{'PACKAGES'} . "/All" if (!$packages && $ENV{'PACKAGES'}); | | 72 | $packages = $ENV{'PACKAGES'} . "/All" if (!$packages && $ENV{'PACKAGES'}); |
73 | $packages = $pkgsrcdir . "/packages/All" if (!$packages); | | 73 | $packages = $pkgsrcdir . "/packages/All" if (!$packages); |
74 | $rebuild &&= $opt{t} || "install"; | | 74 | $rebuild &&= $opt{t} || "install"; |
75 | $clean = "clean" if ($opt{c}); | | 75 | $clean = "clean" if ($opt{c}); |
76 | $clean = "CLEANDEPENDS=YES clean" if ($opt{C}); | | 76 | $clean = "CLEANDEPENDS=YES clean" if ($opt{C}); |
77 | $make ||= $ENV{'MAKE'} || "make"; | | 77 | $make ||= $ENV{'MAKE'} || "make"; |
78 | | | 78 | |
79 | my(@pkgs, $pkg, $req, %req, %dep, @reqs, @rreqs); | | 79 | my(@pkgs, $pkg, $req, %req, %dep, @reqs, @rreqs); |
80 | my(%clusters, $cluster); | | 80 | my(%clusters, $cluster); |
81 | my(%where, $pkgcnt, $num, %num, @num, %ord, $suffix); | | 81 | my(%where, $pkgcnt, $num, %num, @num, %ord, $suffix); |
82 | my(%color, $color, %vuln); | | 82 | my(%color, $color, %vuln); |
83 | my(%need, %forced, $label); | | 83 | my(%need, %forced, $label); |
84 | my($recolor, @graph); | | 84 | my($recolor, @graph); |
85 | my(%vpkgs); | | 85 | my(%vpkgs); |
86 | | | 86 | |
87 | # @pkgs - list of all installed pkgs | | 87 | # @pkgs - list of all installed pkgs |
88 | # %req - pkg to ref to hash of pkgs that it requires | | 88 | # %req - pkg to ref to hash of pkgs that it requires |
89 | # %dep - pkg to ref to hash of pkgs that depend on it | | 89 | # %dep - pkg to ref to hash of pkgs that depend on it |
90 | # %clusters - pkg prefix to number of pkgs that share the prefix | | 90 | # %clusters - pkg prefix to number of pkgs that share the prefix |
91 | # %where - pkg to location in source tree | | 91 | # %where - pkg to location in source tree |
92 | # %num/@num - pkg to group number/group number array ref | | 92 | # %num/@num - pkg to group number/group number array ref |
93 | # %ord - pkg to its height in the tree | | 93 | # %ord - pkg to its height in the tree |
94 | # %color - pkg to pkg color (green, yellow, red, etc) | | 94 | # %color - pkg to pkg color (green, yellow, red, etc) |
95 | # %vuln - pkg to vulnerabilities recorded against it | | 95 | # %vuln - pkg to vulnerabilities recorded against it |
96 | # %need - pkg to version required (pkg is out of date) | | 96 | # %need - pkg to version required (pkg is out of date) |
97 | # %forced - pkg marked as "forced" to be out of date | | 97 | # %forced - pkg marked as "forced" to be out of date |
98 | # %vpkgs - pkg is viewable (part of selected subgraph) | | 98 | # %vpkgs - pkg is viewable (part of selected subgraph) |
99 | | | 99 | |
100 | ## | | 100 | ## |
101 | ## load out-of-date or security problem list (if given), or a graph to | | 101 | ## load out-of-date or security problem list (if given), or a graph to |
102 | ## recolor | | 102 | ## recolor |
103 | ## | | 103 | ## |
104 | $recolor = 0; | | 104 | $recolor = 0; |
105 | if (@ARGV || ! -t) { | | 105 | if (@ARGV || ! -t) { |
106 | $usecolor = 1; | | 106 | $usecolor = 1; |
107 | while (<>) { | | 107 | while (<>) { |
108 | if (/^digraph/) { | | 108 | if (/^digraph/) { |
109 | $recolor = 1; | | 109 | $recolor = 1; |
110 | @graph = ($_); | | 110 | @graph = ($_); |
111 | } | | 111 | } |
112 | elsif ($recolor > 0) { | | 112 | elsif ($recolor > 0) { |
113 | push(@graph, $_); | | 113 | push(@graph, $_); |
114 | $recolor++ if (/^subgraph/); | | 114 | $recolor++ if (/^subgraph/); |
115 | $recolor-- if (/^\}/); | | 115 | $recolor-- if (/^\}/); |
116 | $recolor -= ($recolor == 0); | | 116 | $recolor -= ($recolor == 0); |
117 | } | | 117 | } |
118 | elsif (m:^([^/\s]+)\t([^/\s]+/[^/\s]+)\t(\d+[^/\s]*)$:) { | | 118 | elsif (m:^([^/\s]+)\t([^/\s]+/[^/\s]+)\t(\d+[^/\s]*)$:) { |
119 | $where{"$1-$3"} = $2; | | 119 | $where{"$1-$3"} = $2; |
120 | } | | 120 | } |
121 | elsif (/^Version mismatch: '(\S+)' (\S+) vs (\S+)/) { | | 121 | elsif (/^Version mismatch: '(\S+)' (\S+) vs (\S+)/) { |
122 | $color{"$1-$2"} = "red"; | | 122 | $color{"$1-$2"} = "red"; |
123 | $need{"$1-$2"} = "$1-$3"; | | 123 | $need{"$1-$2"} = "$1-$3"; |
124 | } | | 124 | } |
125 | elsif (/^Unknown package: '(\S+)' version (\S+)/) { | | 125 | elsif (/^Unknown package: '(\S+)' version (\S+)/) { |
126 | $color{"$1-$2"} = "purple"; | | 126 | $color{"$1-$2"} = "purple"; |
127 | } | | 127 | } |
128 | elsif (/Package (\S+) has a (\S+) vulnerability/) { | | 128 | elsif (/Package (\S+) has a (\S+) vulnerability/) { |
129 | $vuln{$1} = $2; | | 129 | $vuln{$1} = $2; |
130 | $color{$1} = "red"; | | 130 | $color{$1} = "red"; |
131 | } | | 131 | } |
132 | } | | 132 | } |
133 | } | | 133 | } |
134 | | | 134 | |
135 | ## | | 135 | ## |
136 | ## load pkg list | | 136 | ## load pkg list |
137 | ## | | 137 | ## |
138 | opendir(P, $pkg_dbdir) || die("opendir"); | | 138 | opendir(P, $pkg_dbdir) || die("opendir"); |
139 | @pkgs = grep(/-/ && -d "$pkg_dbdir/$_" && -f "$pkg_dbdir/$_/+BUILD_INFO", | | 139 | @pkgs = grep(/-/ && -d "$pkg_dbdir/$_" && -f "$pkg_dbdir/$_/+BUILD_INFO", |
140 | readdir(P)); | | 140 | readdir(P)); |
141 | closedir(P); | | 141 | closedir(P); |
142 | $pkgcnt = @pkgs; | | 142 | $pkgcnt = @pkgs; |
143 | | | 143 | |
144 | ## | | 144 | ## |
145 | ## where are they needed | | 145 | ## where are they needed |
146 | ## | | 146 | ## |
147 | foreach $pkg (@pkgs) { | | 147 | foreach $pkg (@pkgs) { |
148 | $where{$pkg} ||= $pkg; | | 148 | $where{$pkg} ||= $pkg; |
149 | open(R, "<$pkg_dbdir/$pkg/+BUILD_INFO") || | | 149 | open(R, "<$pkg_dbdir/$pkg/+BUILD_INFO") || |
150 | die("$pkg: +BUILD_INFO: $!\n"); | | 150 | die("$pkg: +BUILD_INFO: $!\n"); |
151 | while (<R>) { | | 151 | while (<R>) { |
152 | if (/^PKGPATH\s*=\s*(\S+)/) { | | 152 | if (/^PKGPATH\s*=\s*(\S+)/) { |
153 | $where{$pkg} = $1 if ($where{$pkg} eq $pkg); | | 153 | $where{$pkg} = $1 if ($where{$pkg} eq $pkg); |
154 | last; | | 154 | last; |
155 | } | | 155 | } |
156 | } | | 156 | } |
157 | close(R); | | 157 | close(R); |
158 | next if (!open(R, "<$pkg_dbdir/$pkg/+REQUIRED_BY")); | | 158 | next if (!open(R, "<$pkg_dbdir/$pkg/+REQUIRED_BY")); |
159 | while ($req = <R>) { | | 159 | while ($req = <R>) { |
160 | chomp($req); | | 160 | chomp($req); |
161 | $req{$req}->{$pkg} = 1; | | 161 | $req{$req}->{$pkg} = 1; |
162 | $dep{$pkg}->{$req} = 1; | | 162 | $dep{$pkg}->{$req} = 1; |
163 | } | | 163 | } |
164 | close(R); | | 164 | close(R); |
165 | } | | 165 | } |
166 | | | 166 | |
167 | ## | | 167 | ## |
168 | ## reset %where based on "better" information, if we have it | | 168 | ## reset %where based on "better" information, if we have it |
169 | ## | | 169 | ## |
170 | foreach $pkg (@pkgs) { | | 170 | foreach $pkg (@pkgs) { |
171 | if ($need{$pkg} && $where{$need{$pkg}}) { | | 171 | if ($need{$pkg} && $where{$need{$pkg}}) { |
172 | $where{$pkg} = $where{$need{$pkg}}; | | 172 | $where{$pkg} = $where{$need{$pkg}}; |
173 | } | | 173 | } |
174 | } | | 174 | } |
175 | | | 175 | |
176 | ## | | 176 | ## |
177 | ## if we're recoloring an existing graph, recolor it now and finish | | 177 | ## if we're recoloring an existing graph, recolor it now and finish |
178 | ## | | 178 | ## |
179 | if ($recolor) { | | 179 | if ($recolor) { |
180 | my(%over, %nver, @label, $ocolor); | | 180 | my(%over, %nver, @label, $ocolor); |
181 | map({ /(.*)-(.*)/ && ($nver{$1} = $2) } @pkgs); | | 181 | map({ /(.*)-(.*)/ && ($nver{$1} = $2) } @pkgs); |
182 | | | 182 | |
183 | foreach (@graph) { | | 183 | foreach (@graph) { |
184 | # we don't recolor edges | | 184 | # we don't recolor edges |
185 | ($pkg) = (/\"([^\"]+)\"/); | | 185 | ($pkg) = (/\"([^\"]+)\"/); |
186 | $pkg =~ s/(.*)-(.*)/$1/; | | 186 | $pkg =~ s/(.*)-(.*)/$1/; |
187 | $over{$pkg} = $2; | | 187 | $over{$pkg} = $2; |
188 | | | 188 | |
189 | if (/, EDGE$/) { | | 189 | if (/, EDGE$/) { |
190 | if (defined($nver{$pkg})) { | | 190 | if (defined($nver{$pkg})) { |
191 | s/color=\"[^\"]+\"/color=\"green\"/; | | 191 | s/color=\"[^\"]+\"/color=\"green\"/; |
192 | } | | 192 | } |
193 | else { | | 193 | else { |
194 | s/color=\"[^\"]+\"/color=\"black\"/; | | 194 | s/color=\"[^\"]+\"/color=\"black\"/; |
195 | } | | 195 | } |
196 | } | | 196 | } |
197 | | | 197 | |
198 | elsif (/label=/) { | | 198 | elsif (/label=/) { |
199 | s/color=\"([^\"]+)\"/color="NEWCOLOR"/; | | 199 | s/color=\"([^\"]+)\"/color="NEWCOLOR"/; |
200 | $ocolor = $1; | | 200 | $ocolor = $1; |
201 | s/label=\"([^\"]+)\"/label="NEWLABEL"/; | | 201 | s/label=\"([^\"]+)\"/label="NEWLABEL"/; |
202 | $label = $1; | | 202 | $label = $1; |
203 | if ($nver{$pkg}) { | | 203 | if ($nver{$pkg}) { |
204 | if ($nver{$pkg} ne $over{$pkg} || $ocolor ne "red") { | | 204 | if ($nver{$pkg} ne $over{$pkg} || $ocolor ne "red") { |
205 | s/NEWCOLOR/green/; | | 205 | s/NEWCOLOR/green/; |
206 | } | | 206 | } |
207 | else { | | 207 | else { |
208 | s/NEWCOLOR/$ocolor/; | | 208 | s/NEWCOLOR/$ocolor/; |
209 | } | | 209 | } |
210 | | | 210 | |
211 | @label = split(/\\n/, $label); | | 211 | @label = split(/\\n/, $label); |
212 | $label = ""; | | 212 | $label = ""; |
213 | | | 213 | |
214 | # "where" tag | | 214 | # "where" tag |
215 | if ($label[0] =~ m:/:) { | | 215 | if ($label[0] =~ m:/:) { |
216 | $label .= "\\n" . shift(@label); | | 216 | $label .= "\\n" . shift(@label); |
217 | } | | 217 | } |
218 | | | 218 | |
219 | # installed pkg | | 219 | # installed pkg |
220 | $label[0] =~ s/(.*$pkg)-\S*$/$1-$nver{$pkg}/ if ($nver{$pkg}); | | 220 | $label[0] =~ s/(.*$pkg)-\S*$/$1-$nver{$pkg}/ if ($nver{$pkg}); |
221 | $label .= "\\n" . shift(@label); | | 221 | $label .= "\\n" . shift(@label); |
222 | | | 222 | |
223 | # "needed" pkg | | 223 | # "needed" pkg |
224 | if ($label[0] =~ /^$pkg-(.*)/) { | | 224 | if ($label[0] =~ /^$pkg-(.*)/) { |
225 | $label .= "\\n$label[0]" if ($1 ne $nver{$pkg}); | | 225 | $label .= "\\n$label[0]" if ($1 ne $nver{$pkg}); |
226 | shift(@label); | | 226 | shift(@label); |
227 | } | | 227 | } |
228 | | | 228 | |
229 | # there shouldn't be anything left, but... | | 229 | # there shouldn't be anything left, but... |
230 | $label .= "\\n" . join("\\n", @label); | | 230 | $label .= "\\n" . join("\\n", @label); |
231 | | | 231 | |
232 | $label =~ s/\\n//; | | 232 | $label =~ s/\\n//; |
233 | } | | 233 | } |
234 | else { | | 234 | else { |
235 | s/NEWCOLOR/black/; | | 235 | s/NEWCOLOR/black/; |
236 | } | | 236 | } |
237 | s/NEWLABEL/$label/; | | 237 | s/NEWLABEL/$label/; |
238 | } | | 238 | } |
239 | print; | | 239 | print; |
240 | } | | 240 | } |
241 | exit(0); | | 241 | exit(0); |
242 | } | | 242 | } |
243 | | | 243 | |
244 | ## | | 244 | ## |
245 | ## eliminate redundancies by deleting edges that are redundant | | 245 | ## eliminate redundancies by deleting edges that are redundant |
246 | ## | | 246 | ## |
247 | foreach $pkg (@pkgs) { | | 247 | foreach $pkg (@pkgs) { |
248 | @reqs = sort(keys %{$req{$pkg}}); | | 248 | @reqs = sort(keys %{$req{$pkg}}); |
249 | @rreqs = recurse(\%req, @reqs); | | 249 | @rreqs = recurse(\%req, @reqs); |
250 | map(delete($req{$pkg}->{$_}), @rreqs); | | 250 | map(delete($req{$pkg}->{$_}), @rreqs); |
251 | | | 251 | |
252 | @reqs = sort(keys %{$dep{$pkg}}); | | 252 | @reqs = sort(keys %{$dep{$pkg}}); |
253 | @rreqs = recurse(\%dep, @reqs); | | 253 | @rreqs = recurse(\%dep, @reqs); |
254 | map(delete($dep{$pkg}->{$_}), @rreqs); | | 254 | map(delete($dep{$pkg}->{$_}), @rreqs); |
255 | } | | 255 | } |
256 | | | 256 | |
257 | ## | | 257 | ## |
258 | ## create a hash of clusters of package prefixes, with counts. later, | | 258 | ## create a hash of clusters of package prefixes, with counts. later, |
259 | ## clusters that have more than one member can be marked as subgraphs. | | 259 | ## clusters that have more than one member can be marked as subgraphs. |
260 | ## | | 260 | ## |
261 | ## the outer map() iterates over each pkg name after all instances of | | 261 | ## the outer map() iterates over each pkg name after all instances of |
262 | ## _ in the pkg name have been changed to - (for the purposes of | | 262 | ## _ in the pkg name have been changed to - (for the purposes of |
263 | ## accurate clustering). the inner map() breaks each pkg name up into | | 263 | ## accurate clustering). the inner map() breaks each pkg name up into |
264 | ## tokens that end in - and loops over the resulting list, appending | | 264 | ## tokens that end in - and loops over the resulting list, appending |
265 | ## each one to $a. for example: | | 265 | ## each one to $a. for example: |
266 | ## | | 266 | ## |
267 | ## pkg: one_two-three-4.56 | | 267 | ## pkg: one_two-three-4.56 |
268 | ## tokens: one- two- three- | | 268 | ## tokens: one- two- three- |
269 | ## $a: one- one-two- one-two-three- | | 269 | ## $a: one- one-two- one-two-three- |
270 | ## | | 270 | ## |
271 | map({ $a = ""; | | 271 | map({ $a = ""; |
272 | ($b = $_) =~ s/_/-/g; | | 272 | ($b = $_) =~ s/_/-/g; |
273 | map({ $a .= $_; $clusters{$a}++; } | | 273 | map({ $a .= $_; $clusters{$a}++; } |
274 | $b =~ /([^-]*-)/g); } | | 274 | $b =~ /([^-]*-)/g); } |
275 | @pkgs); | | 275 | @pkgs); |
276 | | | 276 | |
277 | ## | | 277 | ## |
278 | ## impose some sort of order on the pkgs by assigning them numbers | | 278 | ## impose some sort of order on the pkgs by assigning them numbers |
279 | ## that indicate their height in the graph. leaf pkgs will always | | 279 | ## that indicate their height in the graph. leaf pkgs will always |
280 | ## have an order of 1, and each pkg above will be numbered at least 2 | | 280 | ## have an order of 1, and each pkg above will be numbered at least 2 |
281 | ## (possibly higher, if there exists another longer path to another | | 281 | ## (possibly higher, if there exists another longer path to another |
282 | ## leaf). | | 282 | ## leaf). |
283 | ## | | 283 | ## |
284 | map(order(1, $_), @pkgs); | | 284 | map(order(1, $_), @pkgs); |
285 | | | 285 | |
286 | ## | | 286 | ## |
287 | ## assign each pkg a group number, and count the number of pkgs in | | 287 | ## assign each pkg a group number, and count the number of pkgs in |
288 | ## that group. the group numbers are arbitrary, and serve only to | | 288 | ## that group. the group numbers are arbitrary, and serve only to |
289 | ## identify pkgs that belong to the same group. | | 289 | ## identify pkgs that belong to the same group. |
290 | ## | | 290 | ## |
291 | $num = 1; | | 291 | $num = 1; |
292 | foreach $pkg (@pkgs) { | | 292 | foreach $pkg (@pkgs) { |
293 | my($pkgnum); | | 293 | my($pkgnum); |
294 | # my direct requirements | | 294 | # my direct requirements |
295 | @reqs = sort(keys %{$req{$pkg}}); | | 295 | @reqs = sort(keys %{$req{$pkg}}); |
296 | # all the requirements of my requirements | | 296 | # all the requirements of my requirements |
297 | @rreqs = recurse(\%req, @reqs); | | 297 | @rreqs = recurse(\%req, @reqs); |
298 | # the lowest group number from all of those | | 298 | # the lowest group number from all of those |
299 | $pkgnum = number($pkg, @reqs, @rreqs) || $num; | | 299 | $pkgnum = number($pkg, @reqs, @rreqs) || $num; |
300 | # stuff all those into the list for that group | | 300 | # stuff all those into the list for that group |
301 | push(@{$num[$pkgnum]}, $pkg, @reqs, @rreqs); | | 301 | push(@{$num[$pkgnum]}, $pkg, @reqs, @rreqs); |
302 | # now check for packages coming from other groups | | 302 | # now check for packages coming from other groups |
303 | foreach $req ($pkg, @reqs, @rreqs) { | | 303 | foreach $req ($pkg, @reqs, @rreqs) { |
304 | # no group yet, skip on | | 304 | # no group yet, skip on |
305 | next if (!$num{$req}); | | 305 | next if (!$num{$req}); |
306 | # was $req in a different group | | 306 | # was $req in a different group |
307 | if ($num{$req} != $pkgnum) { | | 307 | if ($num{$req} != $pkgnum) { |
308 | # yes, pull that group into the current group | | 308 | # yes, pull that group into the current group |
309 | push(@{$num[$pkgnum]}, @{$num[$num{$req}]}); | | 309 | push(@{$num[$pkgnum]}, @{$num[$num{$req}]}); |
310 | # empty out the old group | | 310 | # empty out the old group |
311 | @{$num[$num{$req}]} = (); | | 311 | @{$num[$num{$req}]} = (); |
312 | } | | 312 | } |
313 | } | | 313 | } |
314 | # reduce the group list | | 314 | # reduce the group list |
315 | @{$num[$pkgnum]} = uniq(sort(@{$num[$pkgnum]})); | | 315 | @{$num[$pkgnum]} = uniq(sort(@{$num[$pkgnum]})); |
316 | # make sure all packages in this group know | | 316 | # make sure all packages in this group know |
317 | map($num{$_} = $pkgnum, @{$num[$pkgnum]}); | | 317 | map($num{$_} = $pkgnum, @{$num[$pkgnum]}); |
318 | # skip to next available group number | | 318 | # skip to next available group number |
319 | $num += ($num == $pkgnum); | | 319 | $num += ($num == $pkgnum); |
320 | } | | 320 | } |
321 | | | 321 | |
322 | ## | | 322 | ## |
323 | ## if we want to check a specific pkg for rebuild impact, mark it as | | 323 | ## if we want to check a specific pkg for rebuild impact, mark it as |
324 | ## "forced" to be out of date, unless it already *is* out of date. | | 324 | ## "forced" to be out of date, unless it already *is* out of date. |
325 | ## | | 325 | ## |
326 | if (@outofdate) { | | 326 | if (@outofdate) { |
327 | $usecolor = 1; | | 327 | $usecolor = 1; |
328 | canonicalize(@outofdate); | | 328 | canonicalize(@outofdate); |
329 | | | 329 | |
330 | foreach (@outofdate) { | | 330 | foreach (@outofdate) { |
331 | if ($color{$_} ne "red") { | | 331 | if ($color{$_} ne "red") { |
332 | $color{$_} = "red"; | | 332 | $color{$_} = "red"; |
333 | $need{$_} = $_; | | 333 | $need{$_} = $_; |
334 | $forced{$_} = " (forced)"; | | 334 | $forced{$_} = " (forced)"; |
335 | } | | 335 | } |
336 | } | | 336 | } |
337 | } | | 337 | } |
338 | | | 338 | |
339 | ## | | 339 | ## |
340 | ## if we want to update a specific package, mark all non-related | | 340 | ## if we want to update a specific package, mark all non-related |
341 | ## packages as "green". this avoids rebuilding unnecessary pkgs that | | 341 | ## packages as "green". this avoids rebuilding unnecessary pkgs that |
342 | ## don't depend on any of the same dependencies as the given pkg. if | | 342 | ## don't depend on any of the same dependencies as the given pkg. if |
343 | ## $force is set, mark *all* dependencies of the given pkg as out of | | 343 | ## $force is set, mark *all* dependencies of the given pkg as out of |
344 | ## date. | | 344 | ## date. |
345 | ## | | 345 | ## |
346 | if (@update) { | | 346 | if (@update) { |
347 | my(@leftover); | | 347 | my(@leftover); |
348 | | | 348 | |
349 | canonicalize(@update); | | 349 | canonicalize(@update); |
350 | @update = uniq(sort(@update, recurse(\%req, @update))); | | 350 | @update = uniq(sort(@update, recurse(\%req, @update))); |
351 | | | 351 | |
352 | if ($force) { | | 352 | if ($force) { |
353 | foreach (@update) { | | 353 | foreach (@update) { |
354 | if ($color{$_} ne "red") { | | 354 | if ($color{$_} ne "red") { |
355 | $color{$_} = "red"; | | 355 | $color{$_} = "red"; |
356 | $need{$_} = $_; | | 356 | $need{$_} = $_; |
357 | $forced{$_} = " (forced)"; | | 357 | $forced{$_} = " (forced)"; |
358 | } | | 358 | } |
359 | } | | 359 | } |
360 | } | | 360 | } |
361 | | | 361 | |
362 | foreach (sort(@pkgs)) { | | 362 | foreach (sort(@pkgs)) { |
363 | if ($_ eq $update[0]) { | | 363 | if ($_ eq $update[0]) { |
364 | shift(@update); | | 364 | shift(@update); |
365 | } | | 365 | } |
366 | else { | | 366 | else { |
367 | push(@leftover, $_); | | 367 | push(@leftover, $_); |
368 | } | | 368 | } |
369 | } | | 369 | } |
370 | | | 370 | |
371 | delete(@color{@leftover}); | | 371 | delete(@color{@leftover}); |
372 | delete(@need{@leftover}); | | 372 | delete(@need{@leftover}); |
373 | } | | 373 | } |
374 | | | 374 | |
375 | ## | | 375 | ## |
376 | ## pick packages for a subgraph | | 376 | ## pick packages for a subgraph |
377 | ## | | 377 | ## |
378 | ## + means up from given package, - means down, ++ means all the way | | 378 | ## + means up from given package, - means down, ++ means all the way |
379 | ## up, -- means all the way down, = means all "connected" packages, etc. | | 379 | ## up, -- means all the way down, = means all "connected" packages, etc. |
380 | ## | | 380 | ## |
381 | if (@subgraph) { | | 381 | if (@subgraph) { |
382 | my ($sub, $up, $down, $eq); | | 382 | my ($sub, $up, $down, $eq); |
383 | foreach (@subgraph) { | | 383 | foreach (@subgraph) { |
384 | ($sub) = (/^([-+=]+)/); | | 384 | ($sub) = (/^([-+=]+)/); |
385 | s/^[-+=]+//; | | 385 | s/^[-+=]+//; |
386 | $sub = "+-" if ($sub eq ""); | | 386 | $sub = "+-" if ($sub eq ""); |
387 | canonicalize($_); | | 387 | canonicalize($_); |
388 | $up = join("", ($sub =~ /(\+)/g)); | | 388 | $up = join("", ($sub =~ /(\+)/g)); |
389 | $down = join("", ($sub =~ /(-)/g)); | | 389 | $down = join("", ($sub =~ /(-)/g)); |
390 | $eq = join("", ($sub =~ /(=)/g)); | | 390 | $eq = join("", ($sub =~ /(=)/g)); |
391 | if ($eq) { | | 391 | if ($eq) { |
392 | map($vpkgs{$_} = 1, @{$num[$num{$_}]}); | | 392 | map($vpkgs{$_} = 1, @{$num[$num{$_}]}); |
393 | } | | 393 | } |
394 | else { | | 394 | else { |
395 | if ($up) { | | 395 | if ($up) { |
396 | @reqs = sort(keys %{$req{$_}}); | | 396 | @reqs = sort(keys %{$req{$_}}); |
397 | @rreqs = (length($up) > 1) ? recurse(\%req, @reqs) : (); | | 397 | @rreqs = (length($up) > 1) ? recurse(\%req, @reqs) : (); |
398 | map($vpkgs{$_} = 1, ($_, @reqs, @rreqs)); | | 398 | map($vpkgs{$_} = 1, ($_, @reqs, @rreqs)); |
399 | } | | 399 | } |
400 | if ($down) { | | 400 | if ($down) { |
401 | @reqs = sort(keys %{$dep{$_}}); | | 401 | @reqs = sort(keys %{$dep{$_}}); |
402 | @rreqs = (length($down) > 1) ? recurse(\%dep, @reqs) : (); | | 402 | @rreqs = (length($down) > 1) ? recurse(\%dep, @reqs) : (); |
403 | map($vpkgs{$_} = 1, ($_, @reqs, @rreqs)); | | 403 | map($vpkgs{$_} = 1, ($_, @reqs, @rreqs)); |
404 | } | | 404 | } |
405 | } | | 405 | } |
406 | } | | 406 | } |
407 | } | | 407 | } |
408 | else { | | 408 | else { |
409 | @vpkgs{@pkgs} = (1) x @pkgs; | | 409 | @vpkgs{@pkgs} = (1) x @pkgs; |
410 | } | | 410 | } |
411 | | | 411 | |
412 | ## | | 412 | ## |
413 | ## if checking for rebuild impact, also mark packages that are too | | 413 | ## if checking for rebuild impact, also mark packages that are too |
414 | ## deeply involved as "green" so that they're not candidates for | | 414 | ## deeply involved as "green" so that they're not candidates for |
415 | ## destruction | | 415 | ## destruction |
416 | ## | | 416 | ## |
417 | if (@impact) { | | 417 | if (@impact) { |
418 | my ($impact); | | 418 | my ($impact); |
419 | | | 419 | |
420 | # step 1: canonicalize anything that's not a number (ie, is the | | 420 | # step 1: canonicalize anything that's not a number (ie, is the |
421 | # name of a pkg) and eliminate duplicates (we just don't need 'em) | | 421 | # name of a pkg) and eliminate duplicates (we just don't need 'em) |
422 | foreach (@impact) { | | 422 | foreach (@impact) { |
423 | next if (/^\d+$/); | | 423 | next if (/^\d+$/); |
424 | canonicalize($_); | | 424 | canonicalize($_); |
425 | } | | 425 | } |
426 | @impact = uniq(sort(@impact)); | | 426 | @impact = uniq(sort(@impact)); |
427 | | | 427 | |
428 | # step 2: the "default" impact allows for anything to be rebuilt, | | 428 | # step 2: the "default" impact allows for anything to be rebuilt, |
429 | # but numeric values in @impact are also allowed, so pick the | | 429 | # but numeric values in @impact are also allowed, so pick the |
430 | # lowest one (specifying both 1 and 2 really means just 1) | | 430 | # lowest one (specifying both 1 and 2 really means just 1) |
431 | $impact = $ord{(sort(byord @pkgs))[0]}; | | 431 | $impact = $ord{(sort(byord @pkgs))[0]}; |
432 | while ($impact[0] =~ /^\d+$/) { | | 432 | while ($impact[0] =~ /^\d+$/) { |
433 | $_ = shift(@impact); | | 433 | $_ = shift(@impact); |
434 | $impact = $_ if ($_ < $impact); | | 434 | $impact = $_ if ($_ < $impact); |
435 | } | | 435 | } |
436 | | | 436 | |
437 | # step 3: anything that would have too great an impact on the tree | | 437 | # step 3: anything that would have too great an impact on the tree |
438 | # gets marked (the impactof() function will check the @impact | | 438 | # gets marked (the impactof() function will check the @impact |
439 | # array to avoid specific pkgs being rebuild) | | 439 | # array to avoid specific pkgs being rebuild) |
440 | foreach $pkg (keys %vpkgs) { | | 440 | foreach $pkg (keys %vpkgs) { |
441 | next if (impactof($impact, $pkg) <= $impact); | | 441 | next if (impactof($impact, $pkg) <= $impact); |
442 | $vpkgs{$pkg} = 2; | | 442 | $vpkgs{$pkg} = 2; |
443 | } | | 443 | } |
444 | | | 444 | |
445 | # step 4: anything so marked gets tagged as green. this tagging | | 445 | # step 4: anything so marked gets tagged as green. this tagging |
446 | # is a separate step so that we can properly judge impact over the | | 446 | # is a separate step so that we can properly judge impact over the |
447 | # entire tree (marking too early could prematurely split chunks | | 447 | # entire tree (marking too early could prematurely split chunks |
448 | # that need to be rebuilt) | | 448 | # that need to be rebuilt) |
449 | foreach $pkg (keys %vpkgs) { | | 449 | foreach $pkg (keys %vpkgs) { |
450 | $color{$pkg} = "green" if ($vpkgs{$pkg} == 2); | | 450 | $color{$pkg} = "green" if ($vpkgs{$pkg} == 2); |
451 | } | | 451 | } |
452 | } | | 452 | } |
453 | | | 453 | |
454 | ## | | 454 | ## |
455 | ## translate "older" alternate output modes to the new generic version | | 455 | ## translate "older" alternate output modes to the new generic version |
456 | ## | | 456 | ## |
457 | if ($fetch) { | | 457 | if ($fetch) { |
458 | $target = "fetch"; | | 458 | $target = "fetch"; |
459 | } | | 459 | } |
460 | elsif ($rebuild) { | | 460 | elsif ($rebuild) { |
461 | $exists = 1; | | 461 | $exists = 1; |
462 | $limit = 1; | | 462 | $limit = 1; |
463 | $target = $rebuild; | | 463 | $target = $rebuild; |
464 | } | | 464 | } |
465 | elsif ($delete) { | | 465 | elsif ($delete) { |
466 | $all++; | | 466 | $all++; |
467 | $simple = 1; | | 467 | $simple = 1; |
468 | } | | 468 | } |
469 | | | 469 | |
470 | ## | | 470 | ## |
471 | ## "target" output mode, ordered by ascendency | | 471 | ## "target" output mode, ordered by ascendency |
472 | ## | | 472 | ## |
473 | if ($target || $simple) { | | 473 | if ($target || $simple) { |
474 | my(@targets); | | 474 | my(@targets); |
475 | printf("PKG_PATH=\"$packages\"\nexport PKG_PATH\n") | | 475 | printf("PKG_PATH=\"$packages\"\nexport PKG_PATH\n") |
476 | if ($pkgadd && $rebuild); | | 476 | if ($pkgadd && $rebuild); |
477 | @targets = grep((color($_) eq "red" && !$limit) || | | 477 | @targets = grep((color($_) eq "red" && !$limit) || |
478 | (color($_) ne "green" && | | 478 | (color($_) ne "green" && |
479 | ($all || ($ord{$_} == 1 && $limit))) || | | 479 | ($all || ($ord{$_} == 1 && $limit))) || |
480 | ($all > 1), keys %vpkgs); | | 480 | ($all > 1), keys %vpkgs); |
481 | @targets = sort(byord @targets); | | 481 | @targets = sort(byord @targets); |
482 | @targets = reverse(@targets) if (!$reverse); | | 482 | @targets = reverse(@targets) if (!$reverse); |
483 | print_package(@targets); | | 483 | print_package(@targets); |
484 | print("true\n") if (!$simple); | | 484 | print("true\n") if (!$simple); |
485 | exit(0); | | 485 | exit(0); |
486 | } | | 486 | } |
487 | | | 487 | |
488 | ## | | 488 | ## |
489 | ## show left overs as a graph | | 489 | ## show left overs as a graph |
490 | ## | | 490 | ## |
491 | printf("digraph \"%s packages\" {\n", | | 491 | printf("digraph \"%s packages\" {\n", |
492 | $limit ? "out of date" : "installed"); | | 492 | $limit ? "out of date" : "installed"); |
493 | printf("label = \"%s packages %s, generated by %s v%s, on %s\";\n", | | 493 | printf("label = \"%s packages %s, generated by %s v%s, on %s\";\n", |
494 | $limit ? "out of date" : "installed", | | 494 | $limit ? "out of date" : "installed", |
495 | @subgraph ? "subgraph (@subgraph)" : "graph", | | 495 | @subgraph ? "subgraph (@subgraph)" : "graph", |
496 | $iam, $version, scalar(localtime)); | | 496 | $iam, $version, scalar(localtime)); |
497 | foreach $pkg (sort(bynum keys %vpkgs)) { | | 497 | foreach $pkg (sort(bynum keys %vpkgs)) { |
498 | $color = color($pkg); | | 498 | $color = color($pkg); |
499 | next if ($limit && $color eq "green"); | | 499 | next if ($limit && $color eq "green"); |
500 | $label = $pkg; | | 500 | $label = $pkg; |
501 | $label =~ s/(.*)-.*/$1/ if (!$versions); | | 501 | $label =~ s/(.*)-.*/$1/ if (!$versions); |
502 | $label = "($ord{$pkg}) $label" if ($order); | | 502 | $label = "($ord{$pkg}) $label" if ($order); |
503 | $label = "$where{$pkg}\\n$label" if ($locations); | | 503 | $label = "$where{$pkg}\\n$label" if ($locations); |
504 | $label .= "\\n$need{$pkg}$forced{$pkg}" if ($need{$pkg}); | | 504 | $label .= "\\n$need{$pkg}$forced{$pkg}" if ($need{$pkg}); |
505 | if ($vuln{$pkg}) { | | 505 | if ($vuln{$pkg}) { |
506 | $label .= "\\n(no update available)" if (!$need{$pkg}); | | 506 | $label .= "\\n(no update available)" if (!$need{$pkg}); |
507 | $label .= "\\n[$vuln{$pkg}]"; | | 507 | $label .= "\\n[$vuln{$pkg}]"; |
508 | } | | 508 | } |
509 | $suffix = "\t// \#$ord{$pkg}, group $num{$pkg}, " . | | 509 | $suffix = "\t// \#$ord{$pkg}, group $num{$pkg}, " . |
510 | (exists($impactof{$pkg}) ? "impact $impactof{$pkg}, " : "") . | | 510 | (exists($impactof{$pkg}) ? "impact $impactof{$pkg}, " : "") . |
511 | scalar(@{$num[$num{$pkg}]}) . " members, $pkgcnt pkgs"; | | 511 | scalar(@{$num[$num{$pkg}]}) . " members, $pkgcnt pkgs"; |
512 | $suffix .= ", LEAF" if ($ord{$pkg} == 1); | | 512 | $suffix .= ", LEAF" if ($ord{$pkg} == 1); |
513 | | | 513 | |
514 | ## | | 514 | ## |
515 | ## scan the cluster list, but in the opposite order so in the case | | 515 | ## scan the cluster list, but in the opposite order so in the case |
516 | ## of pkgs with a common "multi-token" prefix, we only emit the | | 516 | ## of pkgs with a common "multi-token" prefix, we only emit the |
517 | ## one with the longest name. we have to prepend the names to a | | 517 | ## one with the longest name. we have to prepend the names to a |
518 | ## buffer so that they end up being printed in the reverse of | | 518 | ## buffer so that they end up being printed in the reverse of |
519 | ## discovery order, so that we end up with the "least-specific" | | 519 | ## discovery order, so that we end up with the "least-specific" |
520 | ## subgroup announced first. | | 520 | ## subgroup announced first. |
521 | ## | | 521 | ## |
522 | ($a = $pkg) =~ s/_/-/g; | | 522 | ($a = $pkg) =~ s/_/-/g; |
523 | $b = 1; | | 523 | $b = 1; |
524 | $cluster = ""; | | 524 | $cluster = ""; |
525 | while ($group && $a =~ s/-[^-]+-?$/-/) { | | 525 | while ($group && $a =~ s/-[^-]+-?$/-/) { |
526 | next if ($clusters{$a} == $b); | | 526 | next if ($clusters{$a} == $b); |
527 | $b = $clusters{$a}; | | 527 | $b = $clusters{$a}; |
528 | $cluster = sprintf("subgraph \"cluster_%s\" {\n", substr($a, 0, -1)) . | | 528 | $cluster = sprintf("subgraph \"cluster_%s\" {\n", substr($a, 0, -1)) . |
529 | sprintf("label = \"%s (%d)\";\n", substr($a, 0, -1), $b) . | | 529 | sprintf("label = \"%s (%d)\";\n", substr($a, 0, -1), $b) . |
530 | $cluster; | | 530 | $cluster; |
531 | } | | 531 | } |
532 | print($cluster); | | 532 | print($cluster); |
533 | printf("\"%s\" [color=\"%s\",label=\"%s\"];$suffix\n", $pkg, | | 533 | printf("\"%s\" [color=\"%s\",label=\"%s\"];$suffix\n", $pkg, |
534 | $usecolor ? $color : "black", $label); | | 534 | $usecolor ? $color : "black", $label); |
535 | $cluster =~ s/label = .*\n//g; | | 535 | $cluster =~ s/label = .*\n//g; |
536 | $cluster =~ s/.+\{/\}/g; | | 536 | $cluster =~ s/.+\{/\}/g; |
537 | print($cluster); | | 537 | print($cluster); |
538 | @reqs = sort(keys %{$req{$pkg}}); | | 538 | @reqs = sort(keys %{$req{$pkg}}); |
539 | $suffix =~ s/, LEAF$//; | | 539 | $suffix =~ s/, LEAF$//; |
540 | $suffix .= ", EDGE"; | | 540 | $suffix .= ", EDGE"; |
541 | foreach $req (@reqs) { | | 541 | foreach $req (@reqs) { |
542 | $color = color($req); | | 542 | $color = color($req); |
543 | next if ($limit && $color eq "green"); | | 543 | next if ($limit && $color eq "green"); |
544 | printf("\"%s\" -> \"%s\" [color=\"%s\"];$suffix\n", $req, $pkg, | | 544 | printf("\"%s\" -> \"%s\" [color=\"%s\"];$suffix\n", $req, $pkg, |
545 | $usecolor ? $color : "black"); | | 545 | $usecolor ? $color : "black"); |
546 | } | | 546 | } |
547 | } | | 547 | } |
548 | print("}\n"); | | 548 | print("}\n"); |
549 | | | 549 | |
550 | ## | | 550 | ## |
551 | ## print sh(1) style commands to handle work on a given package, or | | 551 | ## print sh(1) style commands to handle work on a given package, or |
552 | ## just the package name if $simple is set | | 552 | ## just the package name if $simple is set |
553 | ## | | 553 | ## |
554 | sub print_package { | | 554 | sub print_package { |
555 | foreach (@_) { | | 555 | foreach (@_) { |
556 | printf("( pkg_info -qe %s || ", /(.*)-.*/) if ($exists && !$simple); | | 556 | printf("( pkg_info -qe %s || ", /(.*)-.*/) if ($exists && !$simple); |
557 | if ($simple) { | | 557 | if ($simple) { |
558 | print($_); | | 558 | print($_); |
559 | } | | 559 | } |
560 | elsif ($pkgadd) { | | 560 | elsif ($pkgadd) { |
561 | printf("( pkg_add %s.tgz", ($need{$_} || $_)); | | 561 | printf("( pkg_add %s.tgz", ($need{$_} || $_)); |
562 | } | | 562 | } |
563 | else { | | 563 | else { |
564 | print("( cd $pkgsrcdir/$where{$_} && $make $target"); | | 564 | print("( cd $pkgsrcdir/$where{$_} && $make $target"); |
565 | print(" && $make $clean") if ($clean); | | 565 | print(" && $make $clean") if ($clean); |
566 | } | | 566 | } |
567 | if (!$simple) { | | 567 | if (!$simple) { |
568 | print(" )") if ($exists); | | 568 | print(" )") if ($exists); |
569 | print(" ) &&"); | | 569 | print(" ) &&"); |
570 | } | | 570 | } |
571 | print("\n"); | | 571 | print("\n"); |
572 | } | | 572 | } |
573 | } | | 573 | } |
574 | | | 574 | |
575 | ## | | 575 | ## |
576 | ## find all dependencies above or below a given node | | 576 | ## find all dependencies above or below a given node |
577 | ## | | 577 | ## |
578 | sub recurse { | | 578 | sub recurse { |
579 | my(@list, @new, $map); | | 579 | my(@list, @new, $map); |
580 | @list = (); | | 580 | @list = (); |
581 | $map = shift; | | 581 | $map = shift; |
582 | foreach (@_) { | | 582 | foreach (@_) { |
583 | @new = keys %{$map->{$_}}; | | 583 | @new = keys %{$map->{$_}}; |
584 | push(@list, @new, recurse($map, @new)); | | 584 | push(@list, @new, recurse($map, @new)); |
585 | } | | 585 | } |
586 | uniq(sort(@list)); | | 586 | uniq(sort(@list)); |
587 | } | | 587 | } |
588 | | | 588 | |
589 | ## | | 589 | ## |
590 | ## canonicalize a pkg name based on what we have installed | | 590 | ## canonicalize a pkg name based on what we have installed |
591 | ## | | 591 | ## |
592 | sub canonicalize { | | 592 | sub canonicalize { |
593 | my($canon, $pkg); | | 593 | my($canon, $pkg); |
594 | | | 594 | |
595 | foreach $pkg (@_) { | | 595 | foreach $pkg (@_) { |
596 | # attempt to find actual pkg, first by argument given... | | 596 | # attempt to find actual pkg, first by argument given... |
597 | ($canon) = grep($pkg eq $_, @pkgs); | | 597 | ($canon) = grep($pkg eq $_, @pkgs); |
598 | | | 598 | |
599 | # ...then by comparing against the internal list sans version numbers | | 599 | # ...then by comparing against the internal list sans version numbers |
600 | ($canon) = grep(($a = $_) =~ s/(.*)-.*/$1/ && $pkg eq $a, @pkgs) | | 600 | ($canon) = grep(($a = $_) =~ s/(.*)-.*/$1/ && $pkg eq $a, @pkgs) |
601 | if (!defined($canon)); | | 601 | if (!defined($canon)); |
602 | | | 602 | |
603 | die("package '$pkg' not found\n") | | 603 | die("package '$pkg' not found\n") |
604 | if (!defined($canon)); | | 604 | if (!defined($canon)); |
605 | | | 605 | |
606 | $pkg = $canon; | | 606 | $pkg = $canon; |
607 | } | | 607 | } |
608 | | | 608 | |
609 | @_; | | 609 | @_; |
610 | } | | 610 | } |
611 | | | 611 | |
612 | ## | | 612 | ## |
613 | ## lowest group number of a set of packages | | 613 | ## lowest group number of a set of packages |
614 | ## | | 614 | ## |
615 | sub number { | | 615 | sub number { |
616 | my($n, $pkg); | | 616 | my($n, $pkg); |
617 | $n = 0; | | 617 | $n = 0; |
618 | foreach $pkg (@_) { | | 618 | foreach $pkg (@_) { |
619 | $n = $num{$pkg} if ($n == 0 || $num{$pkg} < $n); | | 619 | $n = $num{$pkg} if ($n == 0 || $num{$pkg} < $n); |
620 | } | | 620 | } |
621 | $n + 0; | | 621 | $n + 0; |
622 | } | | 622 | } |
623 | | | 623 | |
624 | ## | | 624 | ## |
625 | ## pick a color based on the color of the dependencies | | 625 | ## pick a color based on the color of the dependencies |
626 | ## | | 626 | ## |
627 | sub color { | | 627 | sub color { |
628 | my($pkg) = @_; | | 628 | my($pkg) = @_; |
629 | if ($color{$pkg}) { | | 629 | if ($color{$pkg}) { |
630 | $color{$pkg}; | | 630 | $color{$pkg}; |
631 | } | | 631 | } |
632 | else { | | 632 | else { |
633 | my($req, @reqs, $color); | | 633 | my($req, @reqs, $color); |
634 | @reqs = sort(keys %{$req{$pkg}}); | | 634 | @reqs = sort(keys %{$req{$pkg}}); |
635 | @reqs = (@reqs, recurse(\%req, @reqs)); | | 635 | @reqs = (@reqs, recurse(\%req, @reqs)); |
636 | $color = "green"; | | 636 | $color = "green"; |
637 | foreach $req (@reqs) { | | 637 | foreach $req (@reqs) { |
638 | if ($color{$req} eq "red") { | | 638 | if ($color{$req} eq "red") { |
639 | return "orange"; | | 639 | return "orange"; |
640 | } | | 640 | } |
641 | elsif ($color{$req} eq "purple") { | | 641 | elsif ($color{$req} eq "purple") { |
642 | $color = "blue"; | | 642 | $color = "blue"; |
643 | } | | 643 | } |
644 | } | | 644 | } |
645 | $color; | | 645 | $color; |
646 | } | | 646 | } |
647 | } | | 647 | } |
648 | | | 648 | |
649 | ## | | 649 | ## |
650 | ## bynum - higher numbers come last | | 650 | ## bynum - higher numbers come last |
651 | ## | | 651 | ## |
652 | sub bynum { | | 652 | sub bynum { |
653 | return $num{$a} <=> $num{$b} || | | 653 | return $num{$a} <=> $num{$b} || |
654 | $a cmp $b; | | 654 | $a cmp $b; |
655 | } | | 655 | } |
656 | | | 656 | |
657 | ## | | 657 | ## |
658 | ## byord - higher orders come first | | 658 | ## byord - higher orders come first |
659 | ## | | 659 | ## |
660 | sub byord { | | 660 | sub byord { |
661 | return $ord{$b} <=> $ord{$a} || | | 661 | return $ord{$b} <=> $ord{$a} || |
662 | $b cmp $a; | | 662 | $b cmp $a; |
663 | } | | 663 | } |
664 | | | 664 | |
665 | ## | | 665 | ## |
666 | ## order - the order of a pkg is one higher than anything below it | | 666 | ## order - the order of a pkg is one higher than anything below it |
667 | ## | | 667 | ## |
668 | sub order { | | 668 | sub order { |
669 | my($n, @pkgs) = @_; | | 669 | my($n, @pkgs) = @_; |
670 | my($pkg); | | 670 | my($pkg); |
671 | foreach $pkg (@pkgs) { | | 671 | foreach $pkg (@pkgs) { |
672 | $ord{$pkg} = $n if ($ord{$pkg} <= $n); | | 672 | $ord{$pkg} = $n if ($ord{$pkg} <= $n); |
673 | order($n + 1, sort(keys %{$req{$pkg}})); | | 673 | order($n + 1, sort(keys %{$req{$pkg}})); |
674 | } | | 674 | } |
675 | } | | 675 | } |
676 | | | 676 | |
677 | ## | | 677 | ## |
678 | ## uniq - eliminate adjacent duplicate entries in an array | | 678 | ## uniq - eliminate adjacent duplicate entries in an array |
679 | ## | | 679 | ## |
680 | sub uniq { | | 680 | sub uniq { |
681 | my($i); | | 681 | my($i); |
682 | for ($i = 0; $i < $#_; ) { | | 682 | for ($i = 0; $i < $#_; ) { |
683 | if ($_[$i] eq $_[$i + 1]) { | | 683 | if ($_[$i] eq $_[$i + 1]) { |
684 | splice(@_, $i, 1); | | 684 | splice(@_, $i, 1); |
685 | } | | 685 | } |
686 | else { | | 686 | else { |
687 | $i++; | | 687 | $i++; |
688 | } | | 688 | } |
689 | } | | 689 | } |
690 | @_; | | 690 | @_; |
691 | } | | 691 | } |
692 | | | 692 | |
693 | ## | | 693 | ## |
694 | ## impactof - impact of pkg delete/rebuild is the longest path (either | | 694 | ## impactof - impact of pkg delete/rebuild is the longest path (either |
695 | ## up or down the tree) that encompasses all things that need | | 695 | ## up or down the tree) that encompasses all things that need |
696 | ## rebuilding | | 696 | ## rebuilding |
697 | ## | | 697 | ## |
698 | sub impactof { | | 698 | sub impactof { |
699 | my ($impact, $pkg) = @_; | | 699 | my ($impact, $pkg) = @_; |
700 | my (@in, @out); | | 700 | my (@in, @out); |
701 | | | 701 | |
702 | # if we already know or it's dead-simple, get out early | | 702 | # if we already know or it's dead-simple, get out early |
703 | return $impactof{$pkg} if (exists($impactof{$pkg})); | | 703 | return $impactof{$pkg} if (exists($impactof{$pkg})); |
704 | return $impactof{$pkg} = 0 if (color($pkg) eq "green"); | | 704 | return $impactof{$pkg} = 0 if (color($pkg) eq "green"); |
705 | | | 705 | |
706 | # starting with the given pkg, repeatedly look up and down the | | 706 | # starting with the given pkg, repeatedly look up and down the |
707 | # tree for connected pkgs that also require a rebuild | | 707 | # tree for connected pkgs that also require a rebuild |
708 | @out = ($pkg); | | 708 | @out = ($pkg); |
709 | do { | | 709 | do { |
710 | @in = @out; | | 710 | @in = @out; |
711 | @out = ($pkg); | | 711 | @out = ($pkg); |
712 | push(@out, grep(color($_) ne "green", recurse(\%dep, @in))); | | 712 | push(@out, grep(color($_) ne "green", recurse(\%dep, @in))); |
713 | push(@out, grep(color($_) ne "green", recurse(\%req, @out))); | | 713 | push(@out, grep(color($_) ne "green", recurse(\%req, @out))); |
714 | @out = uniq(sort(byord @out)); | | 714 | @out = uniq(sort(byord @out)); |
715 | } while (@in != @out); | | 715 | } while (@in != @out); |
716 | | | 716 | |
717 | # check to see if the set of related pkgs intersects with the set | | 717 | # check to see if the set of related pkgs intersects with the set |
718 | # we want to avoid and if so, mark this set as "too expensive" | | 718 | # we want to avoid and if so, mark this set as "too expensive" |
719 | $a = ""; | | 719 | $a = ""; |
720 | if (@impact) { | | 720 | if (@impact) { |
721 | foreach $b (@impact) { | | 721 | foreach $b (@impact) { |
722 | if (grep($_ eq $b, @out)) { | | 722 | if (grep($_ eq $b, @out)) { |
723 | $a = $b; | | 723 | $a = $b; |
724 | $impact++; | | 724 | $impact++; |
725 | last; | | 725 | last; |
726 | } | | 726 | } |
727 | } | | 727 | } |
728 | } | | 728 | } |
729 | | | 729 | |
730 | # if we didn't hit anything, the impact is the one less than the | | 730 | # if we didn't hit anything, the impact is the one less than the |
731 | # highest ordered remotely connected pkg we found (the longest | | 731 | # highest ordered remotely connected pkg we found (the longest |
732 | # path from the top to the bottom of the set to be rebuilt) | | 732 | # path from the top to the bottom of the set to be rebuilt) |
733 | $impact = $ord{$out[0]} - 1 if ($a eq ""); | | 733 | $impact = $ord{$out[0]} - 1 if ($a eq ""); |
734 | @impactof{@in} = ($impact) x @in; | | 734 | @impactof{@in} = ($impact) x @in; |
735 | | | 735 | |
736 | $impactof{$pkg}; | | 736 | $impactof{$pkg}; |
737 | } | | 737 | } |