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