File Coverage

blib/lib/Perl/Repository/APC.pm
Criterion Covered Total %
statement 21 308 6.8
branch 1 144 0.6
condition 0 27 0.0
subroutine 7 23 30.4
pod 12 12 100.0
total 41 514 7.9


line stmt bran cond sub pod time code
1             package Perl::Repository::APC;
2              
3 3     3   1404 use strict;
  3         4  
  3         95  
4 3     3   14 use warnings;
  3         5  
  3         71  
5 3     3   2258 use version;
  3         8607  
  3         21  
6 3     3   586 use Cwd;
  3         6  
  3         688  
7 3     3   16 use File::Spec;
  3         5  
  3         493  
8 3     3   13353 use Module::CoreList 2.14;
  3         199653  
  3         48  
9              
10             my $Id = q$Id: APC.pm 317 2011-03-26 16:59:05Z k $;
11             # our $VERSION = sprintf "2.000_%03d", substr(q$Rev: 317 $,4);
12             our $VERSION = "2.002001";
13             $VERSION =~ s/_//;
14              
15             our %tarballs = (
16             "5.8.1" => {
17             tarfile => "perl-5.8.1.tar.gz",
18             },
19             "5.8.2" => {
20             tarfile => "perl-5.8.2.tar.gz",
21             },
22             "5.8.3" => {
23             tarfile => "perl-5.8.3.tar.gz",
24             },
25             "5.8.4" => {
26             tarfile => "perl-5.8.4.tar.gz",
27             },
28             "5.8.5" => {
29             tarfile => "perl-5.8.5.tar.gz",
30             },
31             "5.8.6" => {
32             tarfile => "perl-5.8.6.tar.gz",
33             },
34             "5.8.7" => {
35             tarfile => "perl-5.8.7.tar.gz",
36             },
37             "5.8.8" => {
38             tarfile => "perl-5.8.8.tar.gz",
39             },
40             "5.9.0" => {
41             tarfile => "perl-5.9.0.tar.gz",
42             },
43             "5.9.1" => {
44             tarfile => "perl-5.9.1.tar.gz",
45             },
46             "5.9.2" => {
47             tarfile => "perl-5.9.2.tar.gz",
48             },
49             "5.9.3" => {
50             tarfile => "perl-5.9.3.tar.gz",
51             },
52             "5.9.4" => {
53             tarfile => "perl-5.9.4.tar.gz",
54             },
55             "5.9.5" => {
56             tarfile => "perl-5.9.5.tar.gz",
57             },
58             "5.10.0" => {
59             tarfile => "perl-5.10.0.tar.gz",
60             },
61             );
62              
63             sub new {
64 1 50   1 1 489 unless (@_ == 2){
65 1         11 require Carp;
66 1         173 Carp::croak(sprintf "Not enough arguments for %s -> new ()\n", __PACKAGE__);
67             }
68 0           my $proto = shift;
69 0   0       my $class = ref $proto || $proto;
70              
71 0           my $dir = shift;
72 0           my $self;
73              
74 0           $self->{DIR} = $dir;
75 0           $self->{APC} = [_apc_struct($dir)];
76              
77 0           bless $self => $class;
78             }
79              
80             sub apcdirs {
81 0     0 1   my($self) = @_;
82 0           @{$self->{APC}};
  0            
83             }
84              
85             sub tarball {
86 0 0   0 1   unless (@_ == 2){
87 0           require Carp;
88 0           Carp::croak(sprintf "Not enough arguments for %s -> tarball ()\n", __PACKAGE__);
89             }
90 0           my($self,$pver) = @_;
91 0 0         unless ($pver){
92 0           require Carp;
93 0           Carp::croak(sprintf "No version argument for %s -> tarball ()\n", __PACKAGE__);
94             }
95              
96 0           my $DIR = File::Spec->catdir($self->{DIR},$pver);
97 0           my $dir;
98 0 0         unless (opendir $dir, $DIR) {
99 0           return $self->_from_additional_tarballs($pver);
100             }
101 0           my(@dirent) = grep !/RC|TRIAL/, grep /^perl.*\.tar\.gz$/, readdir $dir;
102 0           closedir $dir;
103 0 0         if (@dirent>1){
    0          
104 0           die "\aALERT: (\@dirent > 1: @dirent) in $pver" ;
105             } elsif (@dirent==0) {
106 0           return $self->_from_additional_tarballs($pver);
107             }
108 0           $dirent[0];
109             }
110              
111             sub _from_additional_tarballs {
112 0     0     my($self,$pver) = @_;
113 0 0         die "unsupported perl version '$pver'", unless exists $tarballs{$pver};
114 0           my $tarball = $tarballs{$pver}{tarfile};
115 0           my $cwd = Cwd::cwd();
116 0           my $addltar = File::Spec->catfile
117             (
118             $self->{DIR},
119             "additional_tarballs",
120             $tarball,
121             );
122 0 0         if (-f $addltar){
123 0           return $addltar;
124             } else {
125 0           die "tarball '$tarball' not found. Have you mirrored the additional_tarballs directory?\n";
126             }
127             }
128              
129             sub patches {
130 0     0 1   my($self,$ver) = @_;
131 0 0         unless ($ver) {
132 0           require Carp;
133 0           Carp::confess("patches called without ver[$ver]");
134             }
135 0           my @res;
136 0           for my $apcdir (@{$self->{APC}}) {
  0            
137 0           my $pver = $apcdir->{perl};
138 0 0         next unless $pver eq $ver;
139 0           @res = @{$apcdir->{patches}};
  0            
140 0           last;
141             }
142 0           \@res;
143             }
144              
145             sub first_in_branch {
146 0     0 1   my($self,$branch) = @_;
147 0 0         unless (exists $self->{FIRST_IN_BRANCH}) {
148 0           $self->next_in_branch; # initialize
149             }
150 0           my $ret = $self->{FIRST_IN_BRANCH}{$branch};
151 0 0         die "Unknown branch" unless $ret;
152 0           $ret;
153             }
154              
155             sub next_in_branch {
156 0     0 1   my($self,$ver) = @_;
157 0 0         if (not exists $self->{NEXT_IN_BRANCH}) {
158 0           my %L = ();
159 0           for my $apcdir (@{$self->{APC}}) {
  0            
160 0           my $pbranch = $apcdir->{branch};
161 0           my $pver = $apcdir->{perl};
162 0           $self->{NEXT_IN_BRANCH}{$pver} = [$pbranch]; # only for the last
163 0 0         if ($L{$pbranch}){
164 0           $self->{NEXT_IN_BRANCH}{$L{$pbranch}} = [$pbranch,$pver];
165             } else {
166 0           $self->{FIRST_IN_BRANCH}{$pbranch} = $pver;
167             }
168 0           $L{$pbranch} = $pver;
169             }
170             }
171 0 0         return unless $ver;
172 0           my $ref = $self->{NEXT_IN_BRANCH}{$ver};
173 0 0         die "Unknown perl version $ver\n" unless $ref;
174 0           my($rbranch,$rver) = @$ref;
175             # warn "rver[$rver] rbranch[$rbranch]";
176 0           $rver;
177             }
178              
179             sub get_to_version {
180 0 0   0 1   die "Usage: ->get_to_version(\$branch,\$patch)" unless @_ == 3;
181 0           my($self,$branch,$patch) = @_;
182 0 0         unless ($patch) {
183 0           require Carp;
184 0           Carp::confess("get_to_version called without patch[$patch]");
185             }
186 0           my $bp2v = $self->_bp2v;
187 0           my $ret = $bp2v->{$branch,$patch};
188 0 0         unless ($ret){
189 0           require Carp;
190 0           Carp::confess("patch[$patch] not part of branch[$branch]");
191             }
192 0           $ret;
193             }
194              
195             sub get_diff_dir {
196 0 0   0 1   die "Usage: ->get_diff_dir(\$branch,\$patch)" unless @_ == 3;
197 0           my($self,$branch,$patch) = @_;
198 0           my $perl = $self->get_to_version($branch,$patch);
199 0           my $dir = $self->{PERL2DIR}{$perl};
200 0 0         return $dir if $dir;
201 0           my @apc = @{$self->{APC}};
  0            
202 0           for my $apcdir (@apc) {
203 0           my($dir) = $apcdir->{dir};
204 0           my $abs = File::Spec->catdir($self->{DIR},$dir);
205 0 0         unless (-d $abs) {
206 0           $dir = $apcdir->{diffdir};
207 0           $abs = File::Spec->catdir($self->{DIR},$dir);
208 0 0         unless (-d $abs) {
209 0           warn "WARNING: directory '$abs' not found";
210             }
211             }
212 0           my($perl) = $apcdir->{perl};
213 0           $self->{PERL2DIR}{$perl} = $dir;
214             }
215 0 0         die "could not find dir for perl '$self->{PERL2DIR}{$perl}'" unless $self->{PERL2DIR}{$perl};
216 0           return $self->{PERL2DIR}{$perl};
217             }
218              
219             sub _bp2v {
220 0     0     my $self = shift;
221 0 0         unless ($self->{BP2V}) { # branch/patch to version mapping
222 0           my @apc = @{$self->{APC}};
  0            
223 0           for my $apcdir (@apc) {
224 0           my($apc_branch) = $apcdir->{branch};
225 0           my($pver) = $apcdir->{perl};
226 0           my($patches) = $apcdir->{patches};
227 0           for my $p (@$patches) {
228 0           $self->{BP2V}{$apc_branch,$p} = $pver;
229             }
230             }
231             }
232 0           $self->{BP2V};
233             }
234              
235             sub get_from_version {
236 0     0 1   my($self) = shift;
237 0           my($branch,$patch) = @_;
238 0 0         unless ($patch) {
239 0           require Carp;
240 0 0         $patch = "[undef]" unless defined $patch;
241 0           Carp::confess("get_from_version called without patch[$patch]");
242             }
243 0           my $perl = $self->get_to_version(@_);
244 0           my @apc = @{$self->{APC}};
  0            
245 0           my %Ldir = ( "perl" => 0, "maint-5.004" => 0 );
246 0           for my $apc (@apc) {
247 0           my($apc_branch) = $apc->{branch};
248 0 0         next unless $apc_branch eq $branch;
249 0           my($pver) = $apc->{perl};
250 0 0         if ($pver eq $perl) {
251 0 0         if (exists $Ldir{$apc_branch}){
252 0           return $Ldir{$apc_branch};
253             } else {
254 0           $perl =~ s/1$/0/;
255 0           return $perl;
256             }
257             }
258 0           $Ldir{$apc_branch} = $pver;
259             }
260             }
261              
262             {
263             my %ignore = map { ($_ => undef) } qw(4475 32694);
264             sub _ignore_patch_number ($) {
265 0     0     my($patch_number) = @_;
266 0           return exists $ignore{$patch_number};
267             }
268             }
269              
270             sub _apc_struct ($) {
271 0     0     my $APC = shift;
272 0 0         opendir my $APCDH, $APC or die "Could not open APC[$APC]: $!";
273 0           my @apcdir;
274             my %dseen;
275 0           my %living_dirs = (
276             # all the "old" symlinks
277              
278             ## % ls -ld APC/*/diffs(@)
279             ## lrwxrwxrwx 1 sand sand 21 2005-06-09 06:34:12 APC/5.005_04/diffs -> ../perl-5.005xx-diffs/
280             ## lrwxrwxrwx 1 sand sand 19 2008-01-19 15:48:04 APC/5.6.2/diffs -> ../perl-5.6.2-diffs/
281             ## lrwxrwxrwx 1 sand sand 19 2008-01-19 15:48:04 APC/5.6.3/diffs -> ../perl-5.6.x-diffs/
282             ## lrwxrwxrwx 1 sand sand 19 2008-01-19 15:48:04 APC/5.8.1/diffs -> ../perl-5.8.x-diffs/
283             ## lrwxrwxrwx 1 sand sand 21 2008-01-19 15:48:04 APC/5.9.0/diffs -> ../perl-current-diffs/
284              
285             "5.005_04" => "perl-5.005xx-diffs",
286             "5.6.2" => "perl-5.6.2-diffs",
287             "5.6.3" => "perl-5.6.x-diffs",
288             "5.8.1" => "perl-5.8.x-diffs",
289             "5.9.0" => "perl-current-diffs",
290            
291             # plus the not symlinked ones
292             "5.10.1" => "perl-5.10.x-diffs",
293             );
294 0           my %have_visited;
295 0           DIRENT: for my $dirent (readdir $APCDH, keys %living_dirs) {
296 0 0         next DIRENT unless $dirent =~ /^5/;
297 0           my $diffdir;
298 0 0         if (my $living_dir = $living_dirs{$dirent}) {
299 0           $diffdir = File::Spec->catdir($APC,$living_dir);
300 0 0         unless (-e $diffdir) {
301 0           my $fallback = File::Spec->catdir($APC,$dirent,"diffs");
302 0           warn "Warning: expected to find '$diffdir', trying '$fallback'";
303 0           $diffdir = $fallback;
304             }
305             } else {
306 0           $diffdir = File::Spec->catdir($APC,$dirent,"diffs");
307             }
308 0 0         if ($have_visited{$dirent,$diffdir}++){
309             # again is OK if for a different thing
310             # warn "DEBUG: skipping '$diffdir' due to '$dirent'";
311 0           next DIRENT;
312             }
313 0 0         opendir my $DIFFDIR, $diffdir or die "Could not open $diffdir: $!";
314 0           my %patches;
315             # read them and give them a value
316 0           PFILE: for my $dirent2 (readdir $DIFFDIR) {
317 0 0         next PFILE unless $dirent2 =~ /^(\d+)\.gz/;
318 0           my $candnu = $1;
319 0 0         next PFILE if _ignore_patch_number($candnu);
320 0           $patches{$dirent2} = $candnu;
321 0 0         if ($dseen{$dirent2}) {
322             # warn "Duplicate $dirent2 in $diffdir (also in $dseen{$dirent2})\n";
323             } else {
324 0           $dseen{$dirent2} = $diffdir;
325             }
326             }
327 0           closedir $DIFFDIR;
328 0 0         unless (%patches){ # in case they did not mirror something we try to drop it silently
329             # warn "DEBUG: skipping '$dirent' because no entry";
330 0           next DIRENT;
331             }
332 0 0         my @patches = sort { $patches{$a} <=> $patches{$b} || $a cmp $b } keys %patches;
  0            
333 0           my $branch;
334             my $sortdummy;
335 0           PATCH: for my $n (0..$#patches) {
336 0           my $diff;
337 0 0         die unless -e ($diff = File::Spec->catfile($diffdir,$patches[$n]));
338 0 0         ($sortdummy) = $patches[$n] =~ /(\d+)/ unless $sortdummy;
339 0 0         open my $fh, "zcat $diff |" or die;
340 0           local($/) = "\n";
341 0           LINE: while (<$fh>) {
342 0 0         next LINE unless m|^==== //depot/([^/]+)/([^/]+)|;
343 0           $branch = $1;
344 0           my $subbranch = $2; # this limits us to one level. Unlucky.
345 0 0         next LINE unless $branch =~ /maint/;
346 0 0         $branch .= "/$subbranch" unless $subbranch eq "perl";
347 0           last LINE;
348             # print "$dirent|$patches[0]: $_";
349             }
350 0           close $fh;
351 0 0         if ($branch) {
352 0           last PATCH;
353             }
354             }
355 0           my $reldiffdir = File::Spec->abs2rel($diffdir, $APC);
356 0 0         unless ($reldiffdir) {
357 0           warn "DEBUG: no reldiffdir??? dirent[$dirent]diffdir[$diffdir]";
358             }
359 0           push @apcdir, {branch => $branch,
360             dir => $dirent,
361             diffdir => $reldiffdir,
362             perl => $dirent,
363 0           patches => [map {$patches{$patches[$_]}} 0..$#patches],
364             };
365             }
366 0           closedir $APCDH;
367 0           _splice_additional_tarballs(\@apcdir);
368 0           sort { $a->{patches}[-1] <=> $b->{patches}[-1] } @apcdir;
  0            
369             }
370              
371             sub _splice_additional_tarballs ($) {
372 0     0     my($apcdir) = @_;
373 0           my @splicers;
374 0           while (my($k,$v) = each %tarballs) {
375 0           my $version = version->new($k)->numify + 0;
376 0 0         my $x = $Module::CoreList::patchlevel{$version}
377             or die "could not access corelist for '$version' from '$INC{'Module/CoreList.pm'}'";
378 0           push @splicers, {
379             branch => $x->[0],
380             patchlevel => $x->[1],
381             perl => $k,
382             };
383             }
384 0           my $success = 0;
385 0 0         for my $splicer (sort {$a->{branch} cmp $b->{branch}
  0            
386             ||
387             $a->{patchlevel} <=> $b->{patchlevel}
388             } @splicers) {
389 0           APCDIR: for my $i (0..$#$apcdir) {
390 0           my $beq = $apcdir->[$i]{branch} eq $splicer->{branch};
391 0           my $peq = version->new($splicer->{perl}) >= version->new($apcdir->[$i]{perl});
392 0           my $lok = $splicer->{patchlevel} > $apcdir->[$i]{patches}[0];
393 0           my $rok = $splicer->{patchlevel} <= $apcdir->[$i]{patches}[-1];
394 0 0 0       if ($beq && $peq && $lok && $rok) {
      0        
      0        
395 0           my $adir = splice @$apcdir, $i, 1;
396             # the left range is leading to $version_popular
397             # the right range is leading to what it already states
398 0           my(%left, %right);
399 0           for ("branch","dir","diffdir") {
400 0           $left{$_} = $right{$_} = $adir->{$_};
401             }
402 0 0         if ($splicer->{perl} eq $adir->{perl}){
403             # we cannot have two perls with the same name.
404 0           $adir->{perl} .= ".1"; # ouch
405             }
406 0           $left{perl} = $splicer->{perl};
407 0           $right{perl} = $adir->{perl};
408 0           $left{patches} = [];
409 0           $right{patches} = [];
410 0           push @{$left{patches}}, shift @{$adir->{patches}} while $adir->{patches}[0] <= $splicer->{patchlevel};
  0            
  0            
411 0           $right{patches} = $adir->{patches};
412 0           push @$apcdir, \%left, \%right;
413 0           last APCDIR;
414             }
415             }
416             }
417 0           my %rename = (
418             "5.8.1.1" => "5.8.9",
419             "5.9.0.1" => "5.11.0",
420             );
421 0           APCDIR: for my $i (0..$#$apcdir) {
422 0           my $perl = $apcdir->[$i]{perl};
423 0 0         if (my $rename = $rename{$perl}) {
424 0           $apcdir->[$i]{perl} = $rename;
425             }
426             }
427             }
428              
429             sub version_range {
430 0     0 1   my($self,$branch,$lo,$hi) = @_;
431 0           $lo = $self->closest($branch,">",$lo);
432 0           $hi = $self->closest($branch,"<",$hi);
433 0           my @range;
434 0           my @apc = @{$self->{APC}};
  0            
435 0           for my $apcdir (@apc) {
436 0           my($apc_branch) = $apcdir->{branch};
437 0           my($pver) = $apcdir->{perl};
438 0           my($patches) = $apcdir->{patches};
439 0 0         next unless $apc_branch eq $branch;
440 0 0         next unless $lo <= $patches->[-1];
441 0 0         last if $hi < $patches->[0];
442 0           push @range, $pver;
443             }
444 0           \@range;
445             }
446              
447             sub patch_range {
448 0     0 1   my($self,$branch,$lo,$hi) = @_;
449 0           my($vrange,%vrange);
450 0           $vrange = $self->version_range($branch,$lo,$hi);
451 0           @vrange{@$vrange} = ();
452 0           my @range;
453 0           my @apc = @{$self->{APC}};
  0            
454 0           for my $apcdir (@apc) {
455 0 0         next unless exists $vrange{$apcdir->{perl}};
456 0           my($patches) = $apcdir->{patches};
457 0           for my $p (@$patches) {
458 0 0 0       if ($p >= $lo && $p <= $hi) {
459 0           push @range, $p;
460             }
461             }
462             }
463 0           \@range;
464             }
465              
466             sub closest {
467 0     0 1   my($self,$branch,$alt,$wanted) = @_;
468 0           my $closest;
469 0 0         if ($alt eq "<") {
470 0           $closest = 0;
471             } else {
472 0           $closest = 999999999;
473             }
474 0           my @apc = @{$self->{APC}};
  0            
475 0           for my $i (0..$#apc) {
476 0           my $apcdir = $apc[$i];
477 0           my($apc_branch) = $apcdir->{branch};
478 0           my($pver) = $apcdir->{perl};
479 0           my $patches = $apcdir->{patches};
480 0 0         next unless $apc_branch eq $branch;
481 0 0 0       next if $alt eq ">" && $patches->[-1] < $wanted;
482 0 0 0       next if $alt eq "<" && $patches->[0] > $wanted;
483 0 0 0       if ($alt eq ">" && $patches->[0] > $wanted){
    0 0        
484 0 0         $closest = $patches->[0] if $closest > $patches->[0];
485 0           last;
486             } elsif ($alt eq "<" && $patches->[-1] < $wanted) {
487 0           $closest = $patches->[-1];
488 0           next;
489             }
490 0           for my $p (@$patches) {
491 0 0         if ($alt eq "<") {
492 0 0         last if $p > $wanted;
493 0           $closest = $p;
494             } else {
495 0 0         $closest = $p, last if $p >= $wanted;
496             }
497             }
498             }
499 0 0         if ($alt eq "<") {
500 0 0         if ($closest == 0) {
501 0           die "Could not find a patch > 0 and < $wanted";
502             }
503             } else {
504 0 0         if ($closest == 999999999) {
505 0           die "Could not find a patch < 999999999 and > $wanted";
506             }
507             }
508 0           $closest;
509             }
510              
511             1;
512              
513             __END__