File Coverage

blib/lib/CPAN/Unpack.pm
Criterion Covered Total %
statement 79 97 81.4
branch 14 30 46.6
condition 2 6 33.3
subroutine 13 13 100.0
pod 1 3 33.3
total 109 149 73.1


line stmt bran cond sub pod time code
1             package CPAN::Unpack;
2 1     1   767 use strict;
  1         3  
  1         38  
3 1     1   5 use warnings;
  1         2  
  1         32  
4 1     1   1143 use Archive::Extract;
  1         423189  
  1         419  
5 1     1   13 use Fcntl qw(:mode);
  1         2  
  1         390  
6 1     1   7 use File::Basename qw(basename);
  1         2  
  1         48  
7 1     1   6 use File::Find;
  1         2  
  1         46  
8 1     1   6 use File::Path;
  1         2  
  1         47  
9 1     1   1387 use Parse::CPAN::Packages::Fast;
  1         68359  
  1         33  
10 1     1   752 use YAML::Any ();
  1         755  
  1         17  
11 1     1   5 use base qw(Class::Accessor);
  1         2  
  1         869  
12             __PACKAGE__->mk_accessors(qw(cpan destination quiet));
13             $Archive::Extract::PREFER_BIN = 1;
14              
15             our $VERSION = '0.31';
16              
17             sub new {
18 1     1 1 24806 my $class = shift;
19 1         6 my $self = {};
20 1         4 bless $self, $class;
21 1         7 return $self;
22             }
23              
24             sub unpack {
25 1     1 0 86 my $self = shift;
26 1         4 my $counter = 0;
27              
28 1         6 my $cpan = $self->cpan;
29 1 50       52 die "No $cpan" unless -d $cpan;
30              
31 1         6 my $destination = $self->destination;
32 1         116 mkdir $destination;
33 1 50       23 die "No $destination" unless -d $destination;
34              
35 1         7 my $packages_filename = "$cpan/modules/02packages.details.txt.gz";
36 1 50       27 die "No packages at $packages_filename" unless -f $packages_filename;
37              
38 1         5 my %unpacked_versions;
39 1 50       32 if ( -e "$destination/unpacked_versions.yml" ) {
40 0         0 local $/;
41 0         0 open( my $fh, "<", "$destination/unpacked_versions.yml" );
42 0         0 %unpacked_versions = %{ YAML::Any::Load(<$fh>) };
  0         0  
43 0         0 close $fh;
44             }
45              
46             sub fixme {
47 81     81 0 120 my $path = $_;
48 81         1750 my $mode = ( stat($path) )[2];
49 81 100       1004 if ( S_ISDIR($mode) ) {
50 18 50       2418 chmod( ( S_IMODE($mode) | S_IRWXU ), $path )
51             unless ( ( $mode & S_IRWXU ) == S_IRWXU );
52             }
53             }
54 1         19 my $p = Parse::CPAN::Packages::Fast->new($packages_filename);
55 1         5507 foreach my $distribution ( $p->latest_distributions ) {
56 3         334 $counter++;
57 3         42 my $want = "$destination/" . $distribution->dist;
58 3         52 my $archive_filename = "$cpan/authors/id/" . $distribution->prefix;
59              
60 3 50       151 unless ( -f $archive_filename ) {
61 0         0 warn "Archive $archive_filename not found";
62 0         0 next;
63             }
64              
65 3         198 my $unpacked = $unpacked_versions{ $distribution->dist };
66              
67 3 50       28 if ( !defined( $distribution->version ) ) {
68              
69             # This is a bug in Parse::CPAN::Packages (and ::Fast). It affects a few
70             # dozen packages, so use the mtime as version
71 0         0 $unpacked_versions{ $distribution->dist }
72             = "x" . ( stat $archive_filename )[9];
73             } else {
74 3         25 $unpacked_versions{ $distribution->dist }
75             = "x" . $distribution->version;
76             }
77              
78 3 0 33     35 if ( defined($unpacked)
      33        
79             && $unpacked eq $unpacked_versions{ $distribution->dist }
80             && -d $want )
81             {
82 0         0 next;
83             }
84              
85 3 50       76 if ( -d $want ) {
86 0 0       0 print "Deleting old version of " . $distribution->dist . "\n"
87             unless $self->quiet;
88 0         0 rmtree $want;
89             }
90              
91 3 50       20 print "Unpacking " . $distribution->prefix . " ($counter)\n"
92             unless $self->quiet;
93              
94 3         287 my $extract = Archive::Extract->new( archive => $archive_filename );
95 3         2081 my $to = "$destination/test";
96 3         844 rmtree($to);
97 3         263 mkdir($to);
98 3         24 $extract->extract( to => $to );
99              
100             # Fix up broken permissions
101 3         232587 File::Find::find( { wanted => \&fixme, follow => 0, no_chdir => 1 },
102             $to );
103              
104 3         843 my @files = <$to/*>;
105 3         14 my $files = @files;
106 3 50       24 if ( $files == 1 ) {
107 3         11 my $file = $files[0];
108 3 50       62 if ( S_ISDIR( ( stat( $file ) )[2] ) ) {
109 3         399 rename $file, $want;
110             } else {
111 0         0 mkdir $want;
112 0         0 rename $file, "$want/" . basename($file);
113             }
114 3         368 rmdir $to;
115             } else {
116 0         0 rename $to, $want;
117             }
118              
119 3 50       142 unless ( $counter % 500 ) {
120              
121             # Write this every now and then to prevent ^C from killing the list
122 0         0 open( my $fh, ">", "$destination/unpacked_versions.yml.tmp" );
123 0         0 print $fh YAML::Any::Dump( \%unpacked_versions );
124 0         0 close $fh;
125 0         0 rename "$destination/unpacked_versions.yml.tmp",
126             "$destination/unpacked_versions.yml";
127             }
128             }
129              
130 1         113 open( my $fh, ">", "$destination/unpacked_versions.yml.tmp" );
131 1         175 print $fh YAML::Any::Dump( \%unpacked_versions );
132 1         32758 close $fh;
133 1         159 rename "$destination/unpacked_versions.yml.tmp",
134             "$destination/unpacked_versions.yml";
135             }
136              
137             __END__