1# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- 2use strict; 3package CPAN::Queue::Item; 4 5# CPAN::Queue::Item::new ; 6sub new { 7 my($class,@attr) = @_; 8 my $self = bless { @attr }, $class; 9 return $self; 10} 11 12sub as_string { 13 my($self) = @_; 14 $self->{qmod}; 15} 16 17# r => requires, b => build_requires, c => commandline 18sub reqtype { 19 my($self) = @_; 20 $self->{reqtype}; 21} 22 23sub optional { 24 my($self) = @_; 25 $self->{optional}; 26} 27 28package CPAN::Queue; 29 30# One use of the queue is to determine if we should or shouldn't 31# announce the availability of a new CPAN module 32 33# Now we try to use it for dependency tracking. For that to happen 34# we need to draw a dependency tree and do the leaves first. This can 35# easily be reached by running CPAN.pm recursively, but we don't want 36# to waste memory and run into deep recursion. So what we can do is 37# this: 38 39# CPAN::Queue is the package where the queue is maintained. Dependencies 40# often have high priority and must be brought to the head of the queue, 41# possibly by jumping the queue if they are already there. My first code 42# attempt tried to be extremely correct. Whenever a module needed 43# immediate treatment, I either unshifted it to the front of the queue, 44# or, if it was already in the queue, I spliced and let it bypass the 45# others. This became a too correct model that made it impossible to put 46# an item more than once into the queue. Why would you need that? Well, 47# you need temporary duplicates as the manager of the queue is a loop 48# that 49# 50# (1) looks at the first item in the queue without shifting it off 51# 52# (2) cares for the item 53# 54# (3) removes the item from the queue, *even if its agenda failed and 55# even if the item isn't the first in the queue anymore* (that way 56# protecting against never ending queues) 57# 58# So if an item has prerequisites, the installation fails now, but we 59# want to retry later. That's easy if we have it twice in the queue. 60# 61# I also expect insane dependency situations where an item gets more 62# than two lives in the queue. Simplest example is triggered by 'install 63# Foo Foo Foo'. People make this kind of mistakes and I don't want to 64# get in the way. I wanted the queue manager to be a dumb servant, not 65# one that knows everything. 66# 67# Who would I tell in this model that the user wants to be asked before 68# processing? I can't attach that information to the module object, 69# because not modules are installed but distributions. So I'd have to 70# tell the distribution object that it should ask the user before 71# processing. Where would the question be triggered then? Most probably 72# in CPAN::Distribution::rematein. 73 74use vars qw{ @All $VERSION }; 75$VERSION = "5.5003"; 76 77# CPAN::Queue::queue_item ; 78sub queue_item { 79 my($class,@attr) = @_; 80 my $item = "$class\::Item"->new(@attr); 81 $class->qpush($item); 82 return 1; 83} 84 85# CPAN::Queue::qpush ; 86sub qpush { 87 my($class,$obj) = @_; 88 push @All, $obj; 89 CPAN->debug(sprintf("in new All[%s]", 90 join("",map {sprintf " %s\[%s][%s]\n",$_->{qmod},$_->{reqtype},$_->{optional}} @All), 91 )) if $CPAN::DEBUG; 92} 93 94# CPAN::Queue::first ; 95sub first { 96 my $obj = $All[0]; 97 $obj; 98} 99 100# CPAN::Queue::delete_first ; 101sub delete_first { 102 my($class,$what) = @_; 103 my $i; 104 for my $i (0..$#All) { 105 if ( $All[$i]->{qmod} eq $what ) { 106 splice @All, $i, 1; 107 last; 108 } 109 } 110 CPAN->debug(sprintf("after delete_first mod[%s] All[%s]", 111 $what, 112 join("",map {sprintf " %s\[%s][%s]\n",$_->{qmod},$_->{reqtype},$_->{optional}} @All) 113 )) if $CPAN::DEBUG; 114} 115 116# CPAN::Queue::jumpqueue ; 117sub jumpqueue { 118 my $class = shift; 119 my @what = @_; 120 CPAN->debug(sprintf("before jumpqueue All[%s] what[%s]", 121 join("",map {sprintf " %s\[%s][%s]\n",$_->{qmod},$_->{reqtype},$_->{optional}} @All), 122 join("",map {sprintf " %s\[%s][%s]\n",$_->{qmod},$_->{reqtype},$_->{optional}} @what), 123 )) if $CPAN::DEBUG; 124 unless (defined $what[0]{reqtype}) { 125 # apparently it was not the Shell that sent us this enquiry, 126 # treat it as commandline 127 $what[0]{reqtype} = "c"; 128 } 129 my $inherit_reqtype = $what[0]{reqtype} =~ /^(c|r)$/ ? "r" : "b"; 130 WHAT: for my $what_tuple (@what) { 131 my($qmod,$reqtype,$optional) = @$what_tuple{qw(qmod reqtype optional)}; 132 if ($reqtype eq "r" 133 && 134 $inherit_reqtype eq "b" 135 ) { 136 $reqtype = "b"; 137 } 138 my $jumped = 0; 139 for (my $i=0; $i<$#All;$i++) { #prevent deep recursion 140 if ($All[$i]{qmod} eq $qmod) { 141 $jumped++; 142 } 143 } 144 # high jumped values are normal for popular modules when 145 # dealing with large bundles: XML::Simple, 146 # namespace::autoclean, UNIVERSAL::require 147 CPAN->debug("qmod[$qmod]jumped[$jumped]") if $CPAN::DEBUG; 148 my $obj = "$class\::Item"->new( 149 qmod => $qmod, 150 reqtype => $reqtype, 151 optional => !! $optional, 152 ); 153 unshift @All, $obj; 154 } 155 CPAN->debug(sprintf("after jumpqueue All[%s]", 156 join("",map {sprintf " %s\[%s][%s]\n",$_->{qmod},$_->{reqtype},$_->{optional}} @All) 157 )) if $CPAN::DEBUG; 158} 159 160# CPAN::Queue::exists ; 161sub exists { 162 my($self,$what) = @_; 163 my @all = map { $_->{qmod} } @All; 164 my $exists = grep { $_->{qmod} eq $what } @All; 165 # warn "in exists what[$what] all[@all] exists[$exists]"; 166 $exists; 167} 168 169# CPAN::Queue::delete ; 170sub delete { 171 my($self,$mod) = @_; 172 @All = grep { $_->{qmod} ne $mod } @All; 173 CPAN->debug(sprintf("after delete mod[%s] All[%s]", 174 $mod, 175 join("",map {sprintf " %s\[%s][%s]\n",$_->{qmod},$_->{reqtype},$_->{optional}} @All) 176 )) if $CPAN::DEBUG; 177} 178 179# CPAN::Queue::nullify_queue ; 180sub nullify_queue { 181 @All = (); 182} 183 184# CPAN::Queue::size ; 185sub size { 186 return scalar @All; 187} 188 189sub reqtype_of { 190 my($self,$mod) = @_; 191 my $best = ""; 192 for my $item (grep { $_->{qmod} eq $mod } @All) { 193 my $c = $item->{reqtype}; 194 if ($c eq "c") { 195 $best = $c; 196 last; 197 } elsif ($c eq "r") { 198 $best = $c; 199 } elsif ($c eq "b") { 200 if ($best eq "") { 201 $best = $c; 202 } 203 } else { 204 die "Panic: in reqtype_of: reqtype[$c] seen, should never happen"; 205 } 206 } 207 return $best; 208} 209 210sub iterator { 211 my $i = 0; 212 return sub { 213 until ($All[$i] || $i > $#All) { 214 $i++; 215 } 216 return if $i > $#All; 217 return $All[$i++] 218 }; 219} 220 2211; 222 223__END__ 224 225=head1 NAME 226 227CPAN::Queue - internal queue support for CPAN.pm 228 229=head1 LICENSE 230 231This program is free software; you can redistribute it and/or 232modify it under the same terms as Perl itself. 233 234=cut 235