/[soft]/rpm/urpmi/trunk/urpm/orphans.pm
ViewVC logotype

Contents of /rpm/urpmi/trunk/urpm/orphans.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 4139 - (show annotations) (download)
Thu Apr 19 08:41:37 2012 UTC (12 years ago) by blino
File size: 15186 byte(s)
urpmi: do not print orphans message when no package is orphan
1 package urpm::orphans;
2
3 use strict;
4 use urpm::util;
5 use urpm::msg;
6 use urpm;
7
8 # $Id: select.pm 243120 2008-07-01 12:24:34Z pixel $
9
10 my $fullname2name_re = qr/^(.*)-[^\-]*-[^\-]*\.[^\.\-]*$/;
11
12 #- side-effects: none
13 sub installed_packages_packed {
14 my ($urpm) = @_;
15
16 my $db = urpm::db_open_or_die_($urpm);
17 my @l;
18 $db->traverse(sub {
19 my ($pkg) = @_;
20 $pkg->pack_header;
21 push @l, $pkg;
22 });
23 \@l;
24 }
25
26 #- side-effects: none
27 sub unrequested_list__file {
28 my ($urpm) = @_;
29 ($urpm->{env_dir} || "$urpm->{root}/var/lib/rpm") . '/installed-through-deps.list';
30 }
31 #- side-effects: none
32 sub unrequested_list {
33 my ($urpm) = @_;
34 +{ map {
35 chomp;
36 s/\s+\(.*\)$//;
37 $_ => 1;
38 } cat_(unrequested_list__file($urpm)) };
39 }
40
41 #- side-effects: those of _write_unrequested_list__file
42 sub mark_as_requested {
43 my ($urpm, $state, $test) = @_;
44 my $unrequested = unrequested_list($urpm);
45 my $dirty;
46
47 foreach (keys %{$state->{rejected_already_installed}},
48 grep { $state->{selected}{$_}{requested} } keys %{$state->{selected}}) {
49 my $name = $urpm->{depslist}[$_]->name;
50 if (defined($unrequested->{$name})) {
51 $urpm->{info}(N("Marking %s as manually installed, it won't be auto-orphaned", $name));
52 $dirty = 1;
53 } else {
54 $urpm->{debug}("$name is not in potential orphans") if $urpm->{debug};
55 }
56 delete $unrequested->{$name};
57 }
58
59 if ($dirty && !$test) {
60 _write_unrequested_list__file($urpm, [keys %$unrequested]);
61 }
62 }
63
64 #- side-effects:
65 #- + those of _installed_req_and_unreq_and_update_unrequested_list (<root>/var/lib/rpm/installed-through-deps.list)
66 sub _installed_req_and_unreq {
67 my ($urpm) = @_;
68 my ($req, $unreq, $_unrequested) = _installed_req_and_unreq_and_update_unrequested_list($urpm);
69 ($req, $unreq);
70 }
71 #- side-effects:
72 #- + those of _installed_req_and_unreq_and_update_unrequested_list (<root>/var/lib/rpm/installed-through-deps.list)
73 sub _installed_and_unrequested_lists {
74 my ($urpm) = @_;
75 my ($pkgs, $pkgs2, $unrequested) = _installed_req_and_unreq_and_update_unrequested_list($urpm);
76 push @$pkgs, @$pkgs2;
77 ($pkgs, $unrequested);
78 }
79
80 #- side-effects: <root>/var/lib/rpm/installed-through-deps.list
81 sub _write_unrequested_list__file {
82 my ($urpm, $unreq) = @_;
83
84 $urpm->{info}("writing " . unrequested_list__file($urpm));
85
86 output_safe(unrequested_list__file($urpm),
87 join('', sort map { $_ . "\n" } @$unreq),
88 ".old") if !$urpm->{env_dir};
89 }
90
91 #- side-effects: those of _write_unrequested_list__file
92 sub _installed_req_and_unreq_and_update_unrequested_list {
93 my ($urpm) = @_;
94
95 my $pkgs = installed_packages_packed($urpm);
96
97 $urpm->{debug}("reading and cleaning " . unrequested_list__file($urpm)) if $urpm->{debug};
98 my $unrequested = unrequested_list($urpm);
99 my ($unreq, $req) = partition { $unrequested->{$_->name} } @$pkgs;
100
101 # update the list (to filter dups and now-removed-pkgs)
102 _write_unrequested_list__file($urpm, [map { $_->name } @$unreq]);
103
104 ($req, $unreq, $unrequested);
105 }
106
107 #- returns the new "unrequested" packages
108 #- the reason can be "required by xxx" or "suggested"
109 #-
110 #- side-effects: none
111 sub _selected_unrequested {
112 my ($urpm, $selected, $rejected) = @_;
113
114 require urpm::select;
115 map {
116 if (my $from = $selected->{$_}{from}) {
117 my $pkg = $urpm->{depslist}[$_];
118 my $name = $pkg->name;
119 $pkg->flag_requested || urpm::select::was_pkg_name_installed($rejected, $name) ? () :
120 ($name => "(required by " . $from->fullname . ")");
121 } elsif ($selected->{$_}{suggested}) {
122 ($urpm->{depslist}[$_]->name => "(suggested)");
123 } else {
124 ();
125 }
126 } keys %$selected;
127 }
128
129 #- returns the packages obsoleting packages marked "unrequested"
130 #-
131 #- side-effects: none
132 sub _renamed_unrequested {
133 my ($urpm, $selected, $rejected) = @_;
134
135 my @obsoleted = grep { $rejected->{$_}{obsoleted} } keys %$rejected or return;
136
137 # we have to read the list to know if the old package was marked "unrequested"
138 my $current = unrequested_list($urpm);
139
140 my %l;
141 foreach my $fn (@obsoleted) {
142 my ($n) = $fn =~ $fullname2name_re;
143 $current->{$n} or next;
144
145 my ($new_fn) = keys %{$rejected->{$fn}{closure}};
146 my ($new_n) = $new_fn =~ $fullname2name_re;
147
148 grep { my $pkg = $urpm->{depslist}[$_]; ($pkg->name eq $new_n) && $pkg->flag_installed && $pkg->flag_upgrade } keys %$selected and next;
149 if ($new_n ne $n) {
150 $l{$new_n} = "(obsoletes $fn)";
151 }
152 }
153 %l;
154 }
155 sub new_unrequested {
156 my ($urpm, $state) = @_;
157 (
158 _selected_unrequested($urpm, $state->{selected}, $state->{rejected}),
159 _renamed_unrequested($urpm, $state->{selected}, $state->{rejected}),
160 );
161 }
162 #- side-effects: <root>/var/lib/rpm/installed-through-deps.list
163 sub add_unrequested {
164 my ($urpm, $state) = @_;
165
166 my %l = new_unrequested($urpm, $state);
167 append_to_file(unrequested_list__file($urpm), join('', map { "$_\t\t$l{$_}\n" } keys %l));
168 }
169
170 #- we don't want to check orphans on every auto-select,
171 #- doing it only after many packages have been added
172 #-
173 #- side-effects: none
174 sub check_unrequested_orphans_after_auto_select {
175 my ($urpm) = @_;
176 my $f = unrequested_list__file($urpm);
177 my $nb_added = wc_l($f) - wc_l("$f.old");
178 $nb_added >= $urpm->{options}{'nb-of-new-unrequested-pkgs-between-auto-select-orphans-check'};
179 }
180
181 #- this function computes wether removing $toremove packages will create
182 #- unrequested orphans.
183 #-
184 #- it does not return the new orphans since "whatsuggests" is not available,
185 #- if it detects there are new orphans, _all_unrequested_orphans()
186 #- must be used to have the list of the orphans
187 #-
188 #- side-effects: none
189 sub unrequested_orphans_after_remove {
190 my ($urpm, $toremove) = @_;
191
192 my $db = urpm::db_open_or_die_($urpm);
193 my %toremove = map { $_ => 1 } @$toremove;
194 _unrequested_orphans_after_remove_once($urpm, $db, unrequested_list($urpm), \%toremove);
195 }
196 #- side-effects: none
197 sub _unrequested_orphans_after_remove_once {
198 my ($urpm, $db, $unrequested, $toremove) = @_;
199
200 # first we get the list of requires/suggests that may be unneeded after removing $toremove
201 my @requires;
202 foreach my $fn (keys %$toremove) {
203 my ($n) = $fn =~ $fullname2name_re;
204
205 $db->traverse_tag('name', [ $n ], sub {
206 my ($p) = @_;
207 $p->fullname eq $fn or return;
208 push @requires, $p->requires, $p->suggests;
209 });
210 }
211
212 foreach my $req (uniq(@requires)) {
213 $db->traverse_tag_find('whatprovides', URPM::property2name($req), sub {
214 my ($p) = @_;
215 $toremove->{$p->fullname} and return; # already done
216 $unrequested->{$p->name} or return;
217 $p->provides_overlap($req) or return;
218
219 # cool, $p is "unrequested" and will potentially be newly unneeded
220 if (_will_package_be_unneeded($urpm, $db, $toremove, $p)) {
221 $urpm->{debug}("installed " . $p->fullname . " can now be removed") if $urpm->{debug};
222 return 1;
223 } else {
224 $urpm->{debug}("installed " . $p->fullname . " can not be removed") if $urpm->{debug};
225 }
226 0;
227 }) and return 1;
228 }
229 0;
230 }
231 #- return true if $pkg will no more be required after removing $toremove
232 #-
233 #- nb: it may wrongly return false for complex loops,
234 #- but will never wrongly return true
235 #-
236 #- side-effects: none
237 sub _will_package_be_unneeded {
238 my ($urpm, $db, $toremove, $pkg) = @_;
239
240 my $required_maybe_loop;
241
242 foreach my $prop ($pkg->provides) {
243 _will_prop_still_be_needed($urpm, $db, $toremove,
244 scalar($pkg->fullname), $prop, \$required_maybe_loop)
245 and return;
246 }
247
248 if ($required_maybe_loop) {
249 my ($fullname, @provides) = @$required_maybe_loop;
250 $urpm->{debug}("checking whether $fullname is a dependency loop") if $urpm->{debug};
251
252 # doing it locally, since we may fail (and so we must backtrack this change)
253 my %ignore = %$toremove;
254 $ignore{$pkg->fullname} = 1;
255
256 foreach my $prop (@provides) {
257 #- nb: here we won't loop.
258 _will_prop_still_be_needed($urpm, $db, \%ignore,
259 $fullname, $prop, \$required_maybe_loop)
260 and return;
261 }
262 }
263 1;
264 }
265
266 #- return true if $prop will still be required after removing $toremove
267 #-
268 #- side-effects: none
269 sub _will_prop_still_be_needed {
270 my ($urpm, $db, $toremove, $fullname, $prop, $required_maybe_loop) = @_;
271
272 my ($prov, $range) = URPM::property2name_range($prop) or return;
273
274 $db->traverse_tag_find('whatrequires', $prov, sub {
275 my ($p2) = @_;
276 $toremove->{$p2->fullname} and return 0; # this one is going to be removed, skip it
277
278 foreach ($p2->requires) {
279 my ($pn, $ps) = URPM::property2name_range($_) or next;
280 if ($pn eq $prov && URPM::ranges_overlap($ps, $range)) {
281 #- we found $p2 which requires $prop
282
283 if ($$required_maybe_loop) {
284 $urpm->{debug}(" installed " . $p2->fullname . " still requires " . $fullname) if $urpm->{debug};
285 return 1;
286 }
287 $urpm->{debug}(" installed " . $p2->fullname . " may still requires " . $fullname) if $urpm->{debug};
288 $$required_maybe_loop = [ scalar $p2->fullname, $p2->provides ];
289 }
290 }
291 0;
292 });
293 }
294
295 # so that we can filter out current running kernel:
296 sub _get_current_kernel_package() {
297 my $release = (POSIX::uname())[2];
298 # --qf '%{name}' is used in order to provide the right format:
299 -e "/boot/vmlinuz-$release" && `rpm -qf --qf '%{name}' /boot/vmlinuz-$release`;
300 }
301
302
303 # - returns list of kernels
304 #
305 # _fast_ version w/o looking at all non kernel packages requires on
306 # kernels (like "urpmi_find_leaves '^kernel'" would)
307 #
308 # _all_unrequested_orphans blacklists nearly all kernels b/c of packages
309 # like 'ndiswrapper' or 'basesystem' that requires 'kernel'
310 #
311 # rationale: other packages only require 'kernel' or a sub package we
312 # do not care about (eg: kernel-devel, kernel-firmware, kernel-latest)
313 # so it's useless to look at them
314 #
315 my (@latest_kernels, %requested_kernels, %kernels);
316 sub _kernel_callback {
317 my ($pkg, $unreq_list) = @_;
318 my $shortname = $pkg->name;
319 my $n = $pkg->fullname;
320
321 # only consider kernels (and not main 'kernel' package):
322 # but perform a pass on their requires for dkms like packages that require a specific kernel:
323 if ($shortname !~ /^kernel-/) {
324 foreach (grep { /^kernel/ } $pkg->requires) {
325 $requested_kernels{$_}{$shortname} = $pkg;
326 }
327 return;
328 }
329
330 # only consider real kernels (and not kernel-doc and the like):
331 return if $shortname =~ /-(?:source|doc|headers|firmware(?:|-extra))$/;
332
333 # ignore requested kernels (aka that are not in /var/lib/rpm/installed-through-deps.list)
334 return if !$unreq_list->{$shortname} && $shortname !~ /latest/;
335
336 # keep track of latest kernels in order not to try removing requested kernels:
337 if ($n =~ /latest/) {
338 push @latest_kernels, $pkg->requires;
339 } else {
340 $kernels{$shortname} = $pkg;
341 }
342 }
343
344
345 # - returns list of orphan kernels
346 sub _get_orphan_kernels() {
347 # keep kernels required by kernel-*-latest:
348 delete $kernels{$_} foreach @latest_kernels;
349 # return list of unused/orphan kernels:
350 %kernels;
351 }
352
353
354 #- returns the list of "unrequested" orphans.
355 #-
356 #- side-effects: none
357 sub _all_unrequested_orphans {
358 my ($urpm, $req, $unreq) = @_;
359
360 my (%l, %provides);
361 foreach my $pkg (@$unreq) {
362 $l{$pkg->name} = $pkg;
363 push @{$provides{$_}}, $pkg foreach $pkg->provides_nosense;
364 }
365 my $unreq_list = unrequested_list($urpm);
366
367 my $current_kernel = _get_current_kernel_package();
368
369 while (my $pkg = shift @$req) {
370 # do not do anything regarding kernels if we failed to detect the running one (ie: chroot)
371 _kernel_callback($pkg, $unreq_list) if $current_kernel;
372 foreach my $prop ($pkg->requires, $pkg->suggests) {
373 my $n = URPM::property2name($prop);
374 foreach my $p (@{$provides{$n} || []}) {
375 if ($p != $pkg && $l{$p->name} && $p->provides_overlap($prop)) {
376 delete $l{$p->name};
377 push @$req, $p;
378 }
379 }
380 }
381 }
382
383 # add orphan kernels to the list:
384 my $a = { _get_orphan_kernels() };
385 add2hash_(\%l, $a);
386
387 # add packages that require orphan kernels to the list:
388 foreach (keys %$a) {
389 add2hash_(\%l, $requested_kernels{$_});
390 }
391
392 # do not offer to remove current kernel:
393 delete $l{$current_kernel};
394 [ values %l ];
395 }
396
397
398 #- side-effects: $state->{orphans_to_remove}
399 #- + those of _installed_and_unrequested_lists (<root>/var/lib/rpm/installed-through-deps.list)
400 sub compute_future_unrequested_orphans {
401 my ($urpm, $state) = @_;
402
403 $urpm->{log}("computing unrequested orphans");
404
405 my ($current_pkgs, $unrequested) = _installed_and_unrequested_lists($urpm);
406
407 put_in_hash($unrequested, { new_unrequested($urpm, $state) });
408
409 my %toremove = map { $_ => 1 } URPM::removed_or_obsoleted_packages($state);
410 my @pkgs = grep { !$toremove{$_->fullname} } @$current_pkgs;
411 push @pkgs, map { $urpm->{depslist}[$_] } keys %{$state->{selected} || {}};
412
413 my ($unreq, $req) = partition { $unrequested->{$_->name} } @pkgs;
414
415 $state->{orphans_to_remove} = _all_unrequested_orphans($urpm, $req, $unreq);
416
417 # nb: $state->{orphans_to_remove} is used when computing ->selected_size
418 }
419
420 #- it is quite fast. the slow part is the creation of $installed_packages_packed
421 #- (using installed_packages_packed())
422 #
423 #- side-effects:
424 #- + those of _installed_req_and_unreq (<root>/var/lib/rpm/installed-through-deps.list)
425 sub get_orphans {
426 my ($urpm) = @_;
427
428 $urpm->{log}("computing unrequested orphans");
429
430 my ($req, $unreq) = _installed_req_and_unreq($urpm);
431 _all_unrequested_orphans($urpm, $req, $unreq);
432 }
433
434 sub _get_now_orphans_raw_msg {
435 my ($urpm) = @_;
436
437 my $orphans = get_orphans($urpm);
438 my @orphans = map { scalar $_->fullname } @$orphans or return;
439
440 (scalar(@orphans), add_leading_spaces(join("\n", sort @orphans)));
441 }
442
443 sub get_now_orphans_gui_msg {
444 my ($urpm) = @_;
445
446 my ($count, $list) = _get_now_orphans_raw_msg($urpm) or return;
447 join("\n",
448 P("The following package:\n%s\nis now orphaned.",
449 "The following packages:\n%s\nare now orphaned.", $count, $list),
450 undef,
451 P("You may wish to remove it.",
452 "You may wish to remove them.", $count)
453 );
454 }
455
456 sub get_now_orphans_msg {
457 my ($urpm) = @_;
458
459 my ($count, $list) = _get_now_orphans_raw_msg($urpm) or return;
460 P("The following package:\n%s\nis now orphaned, if you wish to remove it, you can use \"urpme --auto-orphans\"",
461 "The following packages:\n%s\nare now orphaned, if you wish to remove them, you can use \"urpme --auto-orphans\"",
462 $count, $list) . "\n";
463 }
464
465
466 #- side-effects: none
467 sub add_leading_spaces {
468 my ($s) = @_;
469 $s =~ s/^/ /gm;
470 $s;
471 }
472
473 #- side-effects: none
474 sub installed_leaves {
475 my ($urpm, $o_discard) = @_;
476
477 my $packages = installed_packages_packed($urpm);
478
479 my (%l, %provides);
480 foreach my $pkg (@$packages) {
481 next if $o_discard && $o_discard->($pkg);
482 $l{$pkg->name} = $pkg;
483 push @{$provides{$_}}, $pkg foreach $pkg->provides_nosense;
484 }
485
486 foreach my $pkg (@$packages) {
487 foreach my $prop ($pkg->requires, $pkg->suggests) {
488 my $n = URPM::property2name($prop);
489 foreach my $p (@{$provides{$n} || []}) {
490 $p != $pkg && $p->provides_overlap($prop) and
491 delete $l{$p->name};
492 }
493 }
494 }
495
496 [ values %l ];
497 }
498
499 1;

  ViewVC Help
Powered by ViewVC 1.1.30