File Coverage

blib/lib/PPM/Make/Meta.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package PPM::Make::Meta;
2 1     1   756 use strict;
  1         2  
  1         35  
3 1     1   4 use warnings;
  1         1  
  1         24  
4 1     1   26 use PPM::Make::Util qw(:all);
  0            
  0            
5             use File::Find;
6             use Safe;
7             use CPAN::Meta::YAML qw(LoadFile);
8              
9             our $VERSION = '0.9903';
10              
11             sub new {
12             my ($class, %opts) = @_;
13             my $cwd = $opts{dir};
14             die qq{Please supply the name of the directory} unless $cwd;
15             die qq{The supplied directory "$cwd" doesn't exist} unless -d $cwd;
16             my $search = $opts{search};
17             die qq{Please supply a PPM::Make::Search object}
18             unless (defined $search and (ref($search) eq 'PPM::Make::Search'));
19             my $self = {info => {}, cwd => $cwd, search => $search};
20             bless $self, $class;
21             }
22              
23             sub meta {
24             my $self = shift;
25             chdir $self->{cwd} or die qq{Cannot chdir to "$self->{cwd}": $!};
26             my $mb = -e 'Build.PL';
27             $self->{mb} = $mb;
28             $self->parse_yaml if (-e 'META.yml');
29             if ($mb and -d '_build') {
30             $self->parse_build();
31             }
32             else {
33             # $self->parse_makepl();
34             $self->parse_make();
35             }
36             $self->abstract();
37             $self->author();
38             $self->{info}->{VERSION} = (defined $self->{info}->{VERSION_FROM}) ?
39             parse_version($self->{info}->{VERSION_FROM}) :
40             $self->{info}->{VERSION};
41             $self->bundle() if ($self->{info}->{NAME} =~ /^(Bundle|Task)/i);
42             return 1;
43             }
44              
45              
46             sub parse_build {
47             my $self = shift;
48             my $bp = '_build/build_params';
49             # open(my $fh, '<', $bp) or die "Couldn't open $bp: $!";
50             # my @lines = <$fh>;
51             # close $fh;
52             # my $content = join "\n", @lines;
53             # my $c = new Safe();
54             # my $r = $c->reval($content);
55             # if ($@) {
56             # warn "Eval of $bp failed: $@";
57             # return;
58             # }
59             my $file = $self->{cwd} . '/_build/build_params';
60             my $r;
61             unless ($r = do $file) {
62             die "Can't parse $file: $@" if $@;
63             die "Can't do $file: $!" unless defined $r;
64             die "Can't run $file" unless $r;
65             }
66              
67             my $props = $r->[2];
68             my %r = ( NAME => $props->{module_name},
69             DISTNAME => $props->{dist_name},
70             VERSION => $props->{dist_version},
71             VERSION_FROM => $props->{dist_version_from},
72             PREREQ_PM => $props->{requires},
73             AUTHOR => $props->{dist_author},
74             ABSTRACT => $props->{dist_abstract},
75             );
76             foreach (keys %r) {
77             next unless $r{$_};
78             $self->{info}->{$_} = $r{$_};
79             }
80             return 1;
81             }
82              
83             sub parse_yaml {
84             my $self = shift;
85             my $props;
86             eval {$props = LoadFile('META.yml')};
87             return if $@;
88             my $author = ($props->{author} and ref($props->{author}) eq 'ARRAY') ?
89             $props->{author}->[0] : $props->{author};
90             my %r = ( NAME => $props->{name},
91             DISTNAME => $props->{distname},
92             VERSION => $props->{version},
93             VERSION_FROM => $props->{version_from},
94             PREREQ_PM => $props->{requires},
95             AUTHOR => $author,
96             ABSTRACT => $props->{abstract},
97             );
98             foreach (keys %r) {
99             next unless $r{$_};
100             $self->{info}->{$_} ||= $r{$_};
101             }
102             return 1;
103             }
104              
105             sub parse_makepl {
106             my $self = shift;
107             open(my $fh, '<', 'Makefile.PL') or die "Couldn't open Makefile.PL: $!";
108             my @lines = <$fh>;
109             close $fh;
110             my $makeargs;
111             my $content = join "\n", @lines;
112             $content =~ s!\r!!g;
113             $content =~ m!WriteMakefile(\s*\(.*?\bNAME\b.*?\))\s*;!s;
114             unless ($makeargs = $1) {
115             warn "Couldn't extract WriteMakefile args";
116             return;
117             }
118              
119             my $c = new Safe();
120             my %r = $c->reval($makeargs);
121             if ($@) {
122             warn "Eval of Makefile.PL failed: $@";
123             return;
124             }
125             unless ($r{NAME}) {
126             warn "Cannot determine NAME in Makefile.PL";
127             return;
128             }
129             foreach (keys %r) {
130             next unless $r{$_};
131             $self->{info}->{$_} ||= $r{$_};
132             }
133             return 1;
134             }
135              
136             sub parse_make {
137             my $self = shift;
138             my $flag = 0;
139             my @wanted = qw(NAME DISTNAME ABSTRACT ABSTRACT_FROM AUTHOR
140             VERSION VERSION_FROM PREREQ_PM);
141             my $re = join '|', @wanted;
142             my @lines;
143             open(my $fh, '<', 'Makefile') or die "Couldn't open Makefile: $!";
144             while (<$fh>) {
145             if (not $flag and /MakeMaker Parameters/) {
146             $flag = 1;
147             next;
148             }
149             next unless $flag;
150             last if /MakeMaker post_initialize/;
151             next unless /$re/;
152             # Skip MAN3PODS that can appear here if some words from @wanted found
153             next if /^#\s+MAN3PODS => /;
154             chomp;
155             s/^#*\s+// or next;
156             next unless /^(?:$re)\s*\=\>/o;
157             push @lines, $_;
158             }
159             close($fh);
160             my $make = join ',', @lines;
161             $make = '(' . $make . ')';
162             my $c = new Safe();
163             my %r = $c->reval($make);
164             die "Eval of Makefile failed: $@" if ($@);
165             unless ($r{NAME}) {
166             if ($r{NAME} = $r{DISTNAME}) {
167             $r{NAME} =~ s/-/::/gx;
168             warn 'Cannot determine NAME, using DISTNAME instead';
169             }
170             else {
171             die 'Cannot determine NAME and DISTNAME in Makefile';
172             }
173             }
174             for (@wanted) {
175             next unless $r{$_};
176             $self->{info}->{$_} = $r{$_};
177             }
178             return 1;
179             }
180              
181             sub write_makefile {
182             my $self = shift;
183             my $r;
184             my $cwd = $self->{cwd};
185             my $file = 'Makefile.PL';
186             MAKE: {
187             local @ARGV;
188             if (my $makepl_arg = $CPAN::Config->{makepl_arg}) {
189             push @ARGV, (split ' ', $makepl_arg);
190             }
191             unless ($r = do "$cwd/$file") {
192             die "Can't parse $file: $@" if $@;
193             die "Can't do $file: $!" unless defined $r;
194             die "Can't run $file" unless $r;
195             }
196             }
197             my @wanted = qw(NAME DISTNAME ABSTRACT ABSTRACT_FROM AUTHOR
198             VERSION VERSION_FROM PREREQ_PM);
199             my %wanted;
200             foreach (@wanted) {
201             next unless defined $r->{$_};
202             $wanted{$_} = $r->{$_};
203             }
204             $self->{info} = $r;
205             return 1;
206             }
207              
208             sub abstract {
209             my $self = shift;
210             my $info = $self->{info};
211             unless ($info->{ABSTRACT}) {
212             if (my $abstract = $self->guess_abstract()) {
213             warn "Setting ABSTRACT to '$abstract'\n";
214             $self->{info}->{ABSTRACT} = $abstract;
215             }
216             else {
217             warn "Please check ABSTRACT in the ppd file\n";
218             }
219             }
220             }
221              
222             sub guess_abstract {
223             my $self = shift;
224             my $info = $self->{info};
225             my $cwd = $self->{cwd};
226             my $search = $self->{search};
227             my $result;
228             for my $guess(qw(ABSTRACT_FROM VERSION_FROM)) {
229             if (my $file = $info->{$guess}) {
230             print "Trying to get ABSTRACT from $file ...\n";
231             $result = parse_abstract($info->{NAME}, $file);
232             return $result if $result;
233             }
234             }
235             my ($hit, $guess);
236             for my $ext (qw(pm pod)) {
237             if ($info->{NAME} =~ /-|:/) {
238             ($guess = $info->{NAME}) =~ s!.*[-:](.*)!$1.$ext!;
239             }
240             else {
241             $guess = $info->{NAME} . ".$ext";
242             }
243             finddepth(sub{$_ eq $guess && ($hit = $File::Find::name)
244             && ($hit !~ m!blib/!)}, $cwd);
245             next unless ($hit and -f $hit);
246             print "Trying to get ABSTRACT from $hit ...\n";
247             $result = parse_abstract($info->{NAME}, $hit);
248             return $result if $result;
249             }
250             if (my $try = $info->{NAME} || $info->{DISTNAME}) {
251             $try =~ s{-}{::}g;
252             my $mod_results = $search->{mod_results};
253             if (defined $mod_results and defined $mod_results->{$try}) {
254             return $mod_results->{$try}->{mod_abs}
255             if defined $mod_results->{$try}->{mod_abs};
256             }
257             if ($search->search($try, mode => 'mod')) {
258             $mod_results = $search->{mod_results};
259             if (defined $mod_results and defined $mod_results->{$try}) {
260             return $mod_results->{$try}->{mod_abs}
261             if defined $mod_results->{$try}->{mod_abs};
262             }
263             }
264             else {
265             $search->search_error();
266             }
267             }
268             if (my $try = $info->{NAME} || $info->{DISTNAME}) {
269             $try =~ s{::}{-}g;
270             my $dist_results = $search->{dist_results};
271             if (defined $dist_results and defined $dist_results->{$try}) {
272             return $dist_results->{$try}->{dist_abs}
273             if defined $dist_results->{$try}->{dist_abs};
274             }
275             if ($search->search($try, mode => 'dist')) {
276             $dist_results = $search->{dist_results};
277             if (defined $dist_results and defined $dist_results->{$try}) {
278             return $dist_results->{$try}->{dist_abs}
279             if defined $dist_results->{$try}->{dist_abs};
280             }
281             }
282             else {
283             $search->search_error();
284             }
285             }
286             return;
287             }
288              
289             sub bundle {
290             my $self = shift;
291             my $info = $self->{info};
292             my $result = $self->guess_bundle();
293             if ($result and ref($result) eq 'ARRAY') {
294             warn "Extracting Bundle/Task info ...\n";
295             foreach my $mod(@$result) {
296             $info->{PREREQ_PM}->{$mod} = 0;
297             }
298             }
299             else {
300             warn "Please check prerequisites in the ppd file\n";
301             }
302             }
303              
304             sub guess_bundle {
305             my $self = shift;
306             my $info = $self->{info};
307             my $cwd = $self->{cwd};
308             my $result;
309             for my $guess(qw(ABSTRACT_FROM VERSION_FROM)) {
310             if (my $file = $info->{$guess}) {
311             print "Trying to get Bundle/Task info from $file ...\n";
312             $result = parse_bundle($file);
313             return $result if $result;
314             }
315             }
316             my ($hit, $guess);
317             for my $ext (qw(pm pod)) {
318             if ($info->{NAME} =~ /-|:/) {
319             ($guess = $info->{NAME}) =~ s!.*[-:](.*)!$1.$ext!;
320             }
321             else {
322             $guess = $info->{NAME} . ".$ext";
323             }
324             finddepth(sub{$_ eq $guess && ($hit !~ m!blib/!)
325             && ($hit = $File::Find::name) }, $cwd);
326             next unless (-f $hit);
327             print "Trying to get Bundle/Task info from $hit ...\n";
328             $result = parse_bundle($hit);
329             return $result if $result;
330             }
331             return;
332             }
333              
334             sub parse_bundle {
335             my ($file) = @_;
336             my @result;
337             local $/ = "\n";
338             my $in_cont = 0;
339             open(my $fh, '<', $file) or die "Couldn't open $file: $!";
340             while (<$fh>) {
341             $in_cont = m/^=(?!head1\s+CONTENTS)/ ? 0 :
342             m/^=head1\s+CONTENTS/ ? 1 : $in_cont;
343             next unless $in_cont;
344             next if /^=/;
345             s/\#.*//;
346             next if /^\s+$/;
347             chomp;
348             my $result = (split " ", $_, 2)[0];
349             $result =~ s/^L<(.*?)>/$1/;
350             push @result, $result;
351             }
352             close $fh;
353             return (scalar(@result) > 0) ? \@result : undef;
354             }
355              
356             sub author {
357             my $self = shift;
358             my $info = $self->{info};
359             unless ($info->{AUTHOR}) {
360             if (my $author = $self->guess_author()) {
361             $self->{info}->{AUTHOR} = $author;
362             warn qq{Setting AUTHOR to "$author"\n};
363             }
364             else {
365             warn "Please check AUTHOR in the ppd file\n";
366             }
367             }
368             }
369              
370             sub guess_author {
371             my $self = shift;
372             my $info = $self->{info};
373             my $search = $self->{search};
374             my $results;
375             if (my $try = $info->{NAME} || $info->{DISTNAME}) {
376             $try =~ s{-}{::}g;
377             my $mod_results = $search->{mod_results};
378             if (defined $mod_results and defined $mod_results->{$try}) {
379             return $mod_results->{$try}->{author}
380             if defined $mod_results->{$try}->{author};
381             }
382             if ($search->search($try, mode => 'mod')) {
383             $mod_results = $search->{mod_results};
384             if (defined $mod_results and defined $mod_results->{$try}) {
385             return $mod_results->{$try}->{author}
386             if defined $mod_results->{$try}->{author};
387             }
388             }
389             else {
390             $search->search_error();
391             }
392             }
393             if (my $try = $info->{DISTNAME} || $info->{NAME}) {
394             $try =~ s{::}{-}g;
395             my $dist_results = $search->{dist_results};
396             if (defined $dist_results and defined $dist_results->{$try}) {
397             return $dist_results->{$try}->{author}
398             if defined $dist_results->{$try}->{author};
399             }
400             if ($search->search($try, mode => 'dist')) {
401             $dist_results = $search->{dist_results};
402             if (defined $dist_results and defined $dist_results->{$try}) {
403             return $dist_results->{$try}->{author}
404             if defined $dist_results->{$try}->{author};
405             }
406             }
407             else {
408             $search->search_error();
409             }
410             }
411             return;
412             }
413              
414             1;
415              
416             __END__