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   1223 use strict;
  1         2  
  1         52  
3 1     1   5 use warnings;
  1         2  
  1         34  
4 1     1   377 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.9902';
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             push @lines, $_;
157             }
158             close($fh);
159             my $make = join ',', @lines;
160             $make = '(' . $make . ')';
161             my $c = new Safe();
162             my %r = $c->reval($make);
163             die "Eval of Makefile failed: $@" if ($@);
164             unless ($r{NAME}) {
165             if ($r{NAME} = $r{DISTNAME}) {
166             $r{NAME} =~ s/-/::/gx;
167             warn 'Cannot determine NAME, using DISTNAME instead';
168             }
169             else {
170             die 'Cannot determine NAME and DISTNAME in Makefile';
171             }
172             }
173             for (@wanted) {
174             next unless $r{$_};
175             $self->{info}->{$_} = $r{$_};
176             }
177             return 1;
178             }
179              
180             sub write_makefile {
181             my $self = shift;
182             my $r;
183             my $cwd = $self->{cwd};
184             my $file = 'Makefile.PL';
185             MAKE: {
186             local @ARGV;
187             if (my $makepl_arg = $CPAN::Config->{makepl_arg}) {
188             push @ARGV, (split ' ', $makepl_arg);
189             }
190             unless ($r = do "$cwd/$file") {
191             die "Can't parse $file: $@" if $@;
192             die "Can't do $file: $!" unless defined $r;
193             die "Can't run $file" unless $r;
194             }
195             }
196             my @wanted = qw(NAME DISTNAME ABSTRACT ABSTRACT_FROM AUTHOR
197             VERSION VERSION_FROM PREREQ_PM);
198             my %wanted;
199             foreach (@wanted) {
200             next unless defined $r->{$_};
201             $wanted{$_} = $r->{$_};
202             }
203             $self->{info} = $r;
204             return 1;
205             }
206              
207             sub abstract {
208             my $self = shift;
209             my $info = $self->{info};
210             unless ($info->{ABSTRACT}) {
211             if (my $abstract = $self->guess_abstract()) {
212             warn "Setting ABSTRACT to '$abstract'\n";
213             $self->{info}->{ABSTRACT} = $abstract;
214             }
215             else {
216             warn "Please check ABSTRACT in the ppd file\n";
217             }
218             }
219             }
220              
221             sub guess_abstract {
222             my $self = shift;
223             my $info = $self->{info};
224             my $cwd = $self->{cwd};
225             my $search = $self->{search};
226             my $result;
227             for my $guess(qw(ABSTRACT_FROM VERSION_FROM)) {
228             if (my $file = $info->{$guess}) {
229             print "Trying to get ABSTRACT from $file ...\n";
230             $result = parse_abstract($info->{NAME}, $file);
231             return $result if $result;
232             }
233             }
234             my ($hit, $guess);
235             for my $ext (qw(pm pod)) {
236             if ($info->{NAME} =~ /-|:/) {
237             ($guess = $info->{NAME}) =~ s!.*[-:](.*)!$1.$ext!;
238             }
239             else {
240             $guess = $info->{NAME} . ".$ext";
241             }
242             finddepth(sub{$_ eq $guess && ($hit = $File::Find::name)
243             && ($hit !~ m!blib/!)}, $cwd);
244             next unless ($hit and -f $hit);
245             print "Trying to get ABSTRACT from $hit ...\n";
246             $result = parse_abstract($info->{NAME}, $hit);
247             return $result if $result;
248             }
249             if (my $try = $info->{NAME} || $info->{DISTNAME}) {
250             $try =~ s{-}{::}g;
251             my $mod_results = $search->{mod_results};
252             if (defined $mod_results and defined $mod_results->{$try}) {
253             return $mod_results->{$try}->{mod_abs}
254             if defined $mod_results->{$try}->{mod_abs};
255             }
256             if ($search->search($try, mode => 'mod')) {
257             $mod_results = $search->{mod_results};
258             if (defined $mod_results and defined $mod_results->{$try}) {
259             return $mod_results->{$try}->{mod_abs}
260             if defined $mod_results->{$try}->{mod_abs};
261             }
262             }
263             else {
264             $search->search_error();
265             }
266             }
267             if (my $try = $info->{NAME} || $info->{DISTNAME}) {
268             $try =~ s{::}{-}g;
269             my $dist_results = $search->{dist_results};
270             if (defined $dist_results and defined $dist_results->{$try}) {
271             return $dist_results->{$try}->{dist_abs}
272             if defined $dist_results->{$try}->{dist_abs};
273             }
274             if ($search->search($try, mode => 'dist')) {
275             $dist_results = $search->{dist_results};
276             if (defined $dist_results and defined $dist_results->{$try}) {
277             return $dist_results->{$try}->{dist_abs}
278             if defined $dist_results->{$try}->{dist_abs};
279             }
280             }
281             else {
282             $search->search_error();
283             }
284             }
285             return;
286             }
287              
288             sub bundle {
289             my $self = shift;
290             my $info = $self->{info};
291             my $result = $self->guess_bundle();
292             if ($result and ref($result) eq 'ARRAY') {
293             warn "Extracting Bundle/Task info ...\n";
294             foreach my $mod(@$result) {
295             $info->{PREREQ_PM}->{$mod} = 0;
296             }
297             }
298             else {
299             warn "Please check prerequisites in the ppd file\n";
300             }
301             }
302              
303             sub guess_bundle {
304             my $self = shift;
305             my $info = $self->{info};
306             my $cwd = $self->{cwd};
307             my $result;
308             for my $guess(qw(ABSTRACT_FROM VERSION_FROM)) {
309             if (my $file = $info->{$guess}) {
310             print "Trying to get Bundle/Task info from $file ...\n";
311             $result = parse_bundle($file);
312             return $result if $result;
313             }
314             }
315             my ($hit, $guess);
316             for my $ext (qw(pm pod)) {
317             if ($info->{NAME} =~ /-|:/) {
318             ($guess = $info->{NAME}) =~ s!.*[-:](.*)!$1.$ext!;
319             }
320             else {
321             $guess = $info->{NAME} . ".$ext";
322             }
323             finddepth(sub{$_ eq $guess && ($hit !~ m!blib/!)
324             && ($hit = $File::Find::name) }, $cwd);
325             next unless (-f $hit);
326             print "Trying to get Bundle/Task info from $hit ...\n";
327             $result = parse_bundle($hit);
328             return $result if $result;
329             }
330             return;
331             }
332              
333             sub parse_bundle {
334             my ($file) = @_;
335             my @result;
336             local $/ = "\n";
337             my $in_cont = 0;
338             open(my $fh, '<', $file) or die "Couldn't open $file: $!";
339             while (<$fh>) {
340             $in_cont = m/^=(?!head1\s+CONTENTS)/ ? 0 :
341             m/^=head1\s+CONTENTS/ ? 1 : $in_cont;
342             next unless $in_cont;
343             next if /^=/;
344             s/\#.*//;
345             next if /^\s+$/;
346             chomp;
347             my $result = (split " ", $_, 2)[0];
348             $result =~ s/^L<(.*?)>/$1/;
349             push @result, $result;
350             }
351             close $fh;
352             return (scalar(@result) > 0) ? \@result : undef;
353             }
354              
355             sub author {
356             my $self = shift;
357             my $info = $self->{info};
358             unless ($info->{AUTHOR}) {
359             if (my $author = $self->guess_author()) {
360             $self->{info}->{AUTHOR} = $author;
361             warn qq{Setting AUTHOR to "$author"\n};
362             }
363             else {
364             warn "Please check AUTHOR in the ppd file\n";
365             }
366             }
367             }
368              
369             sub guess_author {
370             my $self = shift;
371             my $info = $self->{info};
372             my $search = $self->{search};
373             my $results;
374             if (my $try = $info->{NAME} || $info->{DISTNAME}) {
375             $try =~ s{-}{::}g;
376             my $mod_results = $search->{mod_results};
377             if (defined $mod_results and defined $mod_results->{$try}) {
378             return $mod_results->{$try}->{author}
379             if defined $mod_results->{$try}->{author};
380             }
381             if ($search->search($try, mode => 'mod')) {
382             $mod_results = $search->{mod_results};
383             if (defined $mod_results and defined $mod_results->{$try}) {
384             return $mod_results->{$try}->{author}
385             if defined $mod_results->{$try}->{author};
386             }
387             }
388             else {
389             $search->search_error();
390             }
391             }
392             if (my $try = $info->{DISTNAME} || $info->{NAME}) {
393             $try =~ s{::}{-}g;
394             my $dist_results = $search->{dist_results};
395             if (defined $dist_results and defined $dist_results->{$try}) {
396             return $dist_results->{$try}->{author}
397             if defined $dist_results->{$try}->{author};
398             }
399             if ($search->search($try, mode => 'dist')) {
400             $dist_results = $search->{dist_results};
401             if (defined $dist_results and defined $dist_results->{$try}) {
402             return $dist_results->{$try}->{author}
403             if defined $dist_results->{$try}->{author};
404             }
405             }
406             else {
407             $search->search_error();
408             }
409             }
410             return;
411             }
412              
413             1;
414              
415             __END__