File Coverage

blib/lib/PAR/Dist/FromCPAN.pm
Criterion Covered Total %
statement 30 172 17.4
branch 0 80 0.0
condition 0 15 0.0
subroutine 10 16 62.5
pod 1 1 100.0
total 41 284 14.4


line stmt bran cond sub pod time code
1             package PAR::Dist::FromCPAN;
2              
3 1     1   22446 use 5.006;
  1         4  
  1         29  
4 1     1   5 use strict;
  1         1  
  1         38  
5 1     1   5 use warnings;
  1         1  
  1         38  
6              
7             our $VERSION = '1.11';
8              
9 1     1   916253 use CPAN;
  1         1887791  
  1         596  
10 1     1   2220 use PAR::Dist;
  1         11031  
  1         177  
11 1     1   16 use File::Copy;
  1         11  
  1         139  
12 1     1   7 use Cwd qw/cwd abs_path/;
  1         7  
  1         61  
13 1     1   5 use File::Spec;
  1         5  
  1         20  
14 1     1   6 use File::Path;
  1         2  
  1         77  
15 1     1   4222 use Module::CoreList;
  1         56733  
  1         13  
16              
17             require Exporter;
18              
19             our @ISA = qw(Exporter);
20              
21             our %EXPORT_TAGS = ( 'all' => [ qw(
22             cpan_to_par
23             ) ] );
24              
25             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
26              
27             our @EXPORT = qw(
28             cpan_to_par
29             );
30              
31              
32             our $VERBOSE = 0;
33              
34              
35             sub _verbose {
36 0 0   0     $VERBOSE = shift if (@_);
37 0           return $VERBOSE
38             }
39              
40             sub _diag {
41 0     0     my $msg = shift;
42 0 0         return unless _verbose();
43 0           print $msg ."\n";
44             }
45              
46             sub cpan_to_par {
47 0 0   0 1   die "Uneven number of arguments to 'cpan_to_par'." if @_ % 2;
48 0           my %args = @_;
49            
50 0           _verbose($args{'verbose'});
51              
52 0 0         if (not defined $args{pattern}) {
53 0           die "You need to specify a module pattern.";
54             }
55 0           my $pattern = $args{pattern};
56 0   0       my $skip_ary = $args{skip} || [];
57 0 0 0       my $target_perl = exists($args{perl_version}) ? ($args{perl_version}||0) : $^V;
58              
59 0 0         my $outdir = abs_path(defined($args{out}) ? $args{out} : '.');
60 0 0         die "Output path not a directory." if not -d $outdir;
61              
62 0           _diag "Expanding module pattern.";
63              
64 0 0         my @modules_queue = grep {
65 0           _skip_this($skip_ary, $_->id) ? () : $_
66             } CPAN::Shell->expand('Module', $pattern);
67            
68 0           my %seen;
69             my %seen_multiple_times;
70 0           my @failed;
71              
72 0           my @par_files;
73            
74 0           while (my $mod = shift @modules_queue) {
75              
76 0           my $file = $mod->cpan_file();
77 0 0         if ($seen{$file}) {
78 0           _diag "Skipping previously processed module:\n".$mod->as_glimpse();
79              
80 0           next;
81             }
82 0           $seen{$file}++;
83            
84              
85 0           my $first_in = Module::CoreList->first_release( $mod->id );
86 0 0 0       if ( defined $first_in and $first_in <= $target_perl ) {
87 0           print "Skipping ".$mod->id.". It's been core since $first_in\n";
88 0           next;
89             }
90              
91 0           my $distribution = $mod->distribution;
92 0 0         if (not defined $distribution) {
93 0           warn "Could not get distribution object for module '" . $mod->id . "'! Skipping!";
94 0           next;
95             }
96 0 0         if ( $distribution->isa_perl ) {
97 0           print "Skipping ".$mod->id.". It's only in the core. OOPS\n";
98 0           next;
99             }
100              
101 0           _diag "Processing next module:\n".$mod->as_glimpse();
102              
103             # This branch isn't entered because $mod->make() doesn't
104             # indicate an error if it occurred...
105 0 0 0       if (not $mod->make() and 0) {
106 0           print "Something went wrong making the following module:\n"
107             . $mod->as_glimpse()
108             . "\nWe will try to continue. A summary of all failed modules "
109             . "will be given\nat the end of the script execution in order "
110             . "of appearance.\n";
111 0           push @failed, $mod;
112             }
113              
114             # recursive dependency solving?
115 0 0         if ($args{follow}) {
116 0           _diag "Checking dependencies.";
117 0           my $dist = $mod->distribution;
118 0           my $pre_req = $dist->prereq_pm;
119              
120 0 0         if ($pre_req) {
121 0 0         my @modules =
122             grep {
123 0           _skip_this($skip_ary, $_->id) ? () : $_
124             }
125 0           map {CPAN::Shell->expand('Module', $_)}
126 0 0         grep { $_ !~ /^(?:build_)?requires$/ }
127             # this is a hack, but some users seem to require "requires"
128             # and "build_requires" whereas I only see modules in $pre_req
129             # itself... --Steffen
130 0           keys %{$pre_req->{requires} || {}},
131 0 0         keys %{$pre_req},
132 0           keys %{$pre_req->{build_requires} || {}};
133 0           my %this_seen;
134 0 0 0       @modules =
135             grep {
136 0           $seen{$_->cpan_file}
137             || $this_seen{$_->cpan_file}++ ? 0 : 1
138             }
139             @modules;
140 0           _diag "Recursively adding dependencies for ".$mod->id.": \n"
141 0           . join("\n", map {$_->cpan_file} @modules) . "\n";
142 0 0         if (@modules) {
143             # first we handle the dependencies,
144             # then revisit the module, then process the
145             # rest of the queue
146 0           @modules_queue = (@modules, $mod, @modules_queue);
147             # Email::MIME requires Email::Simple and
148             # Email::Simple require Email::MIME. WTF?
149 0 0         if ($seen_multiple_times{$file}) {
150 0           print "I've processed file '$file' multiple times now.\n"
151             . "I will skip it because it seems to have circular dependencies!\n";
152             }
153             else {
154 0           delete $seen{$file};
155 0           $seen_multiple_times{$file}++;
156             }
157 0           next;
158             }
159             }
160 0           _diag "Finished resolving dependencies for ".$mod->id;
161              
162             }
163              
164             # Run tests?
165 0 0         if ($args{test}) {
166 0           _diag "Running tests.";
167 0           $mod->test();
168             }
169              
170 0           _diag "Building PAR ".$mod->id;
171             # create PAR distro
172 0           my $dir = $mod->distribution->dir;
173 0           _diag "Module was built in '$dir'.";
174              
175 0           chdir($dir);
176 0           my $par_file;
177              
178             # The name of the .par being generated will contain the platform name and
179             # perl version. If the user requested an auto-detection, we potentially
180             # override this with a platform agnostic suffix. Read the PAR::Repository
181             # documentation for an explanation of its meaning.
182 0   0       my $is_platform_agnostic = $args{auto_detect_pure_perl} && _is_pure_perl($dir);
183 0 0         _diag "Distribution seems to be pure-Perl. Building platform agnostic PAR distribution." if $is_platform_agnostic;
184 0 0         eval {
185 0 0         $par_file = ($is_platform_agnostic
186             ? blib_to_par(suffix => "any_arch-any_version.par")
187             : blib_to_par()
188             );
189             } or die "Failed to build PAR distribution $@";
190 0           _diag "Built PAR ".$mod->id." in $par_file";
191 0 0         die "Could not find PAR distribution file '$par_file'."
192             if not -f $par_file;
193              
194 0           _diag "Generated PAR distribution as file '$par_file'";
195 0           _diag "Moving distribution file to output directory '$outdir'.";
196              
197 0 0         unless(File::Copy::move($par_file, $outdir)) {
198 0           die "Could not move file '$par_file' to directory "
199             . "'$outdir'. Reason: $!";
200             }
201 0           $par_file = File::Spec->catfile($outdir, $par_file);
202 0 0         if (-f $par_file) {
203 0           push @par_files, $par_file;
204             }
205             }
206              
207 0 0         if (@failed) {
208 0           print "There were modules that failed to build. "
209             . "These are in order of appearance:\n";
210 0           foreach (@failed) {
211 0           print $_->as_glimpse()."\n";
212             }
213             }
214              
215             # Merge deps
216 0 0         if ($args{merge}) {
217 0           _diag "Merging PAR distributions into one:\n". join(', ', @par_files);
218 0           @par_files = reverse(@par_files); # we resolve dependencies _first.
219 0           merge_par( @par_files );
220 0           foreach my $file (@par_files[1..@par_files-1]) {
221 0           File::Path::rmtree($file);
222             }
223 0           @par_files = ($par_files[0]);
224             }
225              
226             # strip docs
227 0 0         if ($args{strip_docs}) {
228 0           _diag "Removing documentation from the PAR distribution(s).";
229 0           remove_man($_) for @par_files;
230             }
231            
232 0           return(1);
233             }
234              
235             sub _skip_this {
236 0     0     my $ary = shift;
237 0           my $string = shift;
238 0 0         study($string) if @$ary > 2;
239             # print $string.":\n";
240 0           for (@$ary) {
241             # print "--> $_\n";
242             # warn("MATCHES: $string"), sleep(5), return(1) if $string =~ /$_/;
243 0 0         return(1) if $string =~ /$_/;
244             }
245 0           return 0;
246             }
247              
248             sub _is_pure_perl {
249 0     0     my $path = shift;
250 0           my $olddir = Cwd::cwd();
251 0           chdir($path);
252              
253 0           _diag "Checking whether the distribution unpacked in directory '$path' is pure-Perl.";
254              
255 0           my $xs_files = qr/(?i:\.(?:swg|xs|[hic])$)/;
256             # if we can, read manifest to check for telling file names
257 0 0         if (-f 'MANIFEST') {
258 0 0         open my $fh, '<', "MANIFEST" or die "Could not open file MANIFEST for reading: $!";
259 0           while (defined($_=<$fh>)) {
260 0           chomp;
261 0 0         if ($_ =~ $xs_files) {
262 0           _diag "MANIFEST contains the line '$_' which makes me deem the distribution platform-dependent.";
263 0           chdir($olddir);
264 0           return 0;
265             }
266             }
267             }
268              
269             # walk the tree, check for telling file names,
270             # grep for Inline::C
271 0           my $has_xs = 0;
272 0           require File::Find;
273             File::Find::find(
274             sub {
275 0 0   0     return if $has_xs; # short-circuit
276 0           my $file = $_;
277 0 0         if ($file =~ $xs_files) {
278 0           _diag "Directory contains file '$file' which probably makes the distribution platform-dependent.";
279 0           $has_xs = 1;
280 0           return;
281             }
282 0 0         open my $fh, '<', $file
283             or die "Could not open file '$file' for reading while scanning for XS: $!";
284 0           while (defined($_=<$fh>)) {
285 0 0         if (/Inline(?:X::XS|(?:::|\s+)C)/) {
286 0           _diag "File '$file' contains mention of Inline::C => distribution is platform-dependent.";
287 0           $has_xs = 1;
288 0           close($fh);
289 0           return;
290             }
291             }
292 0           close $fh;
293 0           return;
294 0           }, '.'
295             );
296              
297 0           chdir($olddir);
298 0           return !$has_xs;
299             }
300              
301             1;
302             __END__