File Coverage

blib/lib/Module/MakefilePL/Parse.pm
Criterion Covered Total %
statement 89 101 88.1
branch 38 56 67.8
condition n/a
subroutine 11 11 100.0
pod 3 3 100.0
total 141 171 82.4


line stmt bran cond sub pod time code
1             package Module::MakefilePL::Parse;
2            
3 1     1   1126 use 5.006001;
  1         3  
  1         39  
4 1     1   6 use strict;
  1         2  
  1         41  
5 1     1   14 use warnings::register __PACKAGE__;
  1         1  
  1         183  
6            
7             require Exporter;
8 1     1   4 use Carp;
  1         2  
  1         61  
9 1     1   1189 use Text::Balanced qw( extract_bracketed );
  1         44972  
  1         111  
10            
11 1     1   1030 use enum qw(TYPE_MAKEMAKER=1 TYPE_MODULEINSTALL TYPE_MODULEBUILD);
  1         1484  
  1         8  
12            
13             our @ISA = qw(Exporter);
14            
15             our %EXPORT_TAGS = ( 'all' => [ qw( ) ] );
16            
17             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
18            
19             our @EXPORT = qw( );
20            
21             our $VERSION = '0.12';
22            
23             our $DEBUG = 0;
24            
25             sub new {
26 19     19 1 26866 my $class = shift;
27            
28 19         29 my $script = shift;
29            
30 19         109 $script =~ s/\#.*\n/\n/g; # remove comments (not greedy?)
31 19         219 $script =~ s/\s\s+/ /g; # normalize spaces
32            
33 19         65 my $self = {
34             SCRIPT => $script,
35             INSTALLER => undef,
36             };
37            
38 19 100       101 if ($script =~ /use\s+ExtUtils::MakeMaker/) {
    50          
39 17         29 $self->{INSTALLER} = TYPE_MAKEMAKER;
40             }
41             elsif ($script =~ /use\s+(inc::)?Module::Install/) {
42 2         4 $self->{INSTALLER} = TYPE_MODULEINSTALL;
43             }
44             else {
45 0         0 croak "Only scripts which use ExtUtils::MakeMaker or Module::Install are supported";
46             }
47 19         51 bless $self, $class;
48            
49 19         41 $self->{REQUIRED} = $self->_parse;
50 19 100       44 unless ($self->required) {
51 5         51 return;
52             }
53            
54 14         46 return $self;
55             }
56            
57             sub required {
58 32     32 1 46 my $self = shift;
59 32 100       88 if (ref($self->{REQUIRED}) ne 'HASH') {
60 5         15 return;
61             }
62             else {
63 27         155 return $self->{REQUIRED};
64             }
65             }
66            
67             # Cleanup module names (if surrounded by quotes, etc.) and make sure
68             # version is a number.
69            
70             sub _cleanup {
71 14     14   19 my $hashref = shift;
72 14 100       36 if (ref($hashref) eq 'HASH') {
73 13         55 foreach my $module (keys %$hashref) {
74 21         49 my $version = ($hashref->{$module} += 0); # change to number
75 21 100       82 if ($module =~ /[\'\"](.+)[\'\"]/) {
76 4         11 $hashref->{$1} = $version;
77 4         9 delete $hashref->{$module};
78             }
79             }
80 13         49 return $hashref;
81             } else {
82             # TODO: carp "Expected HASH reference", if (warnings::enabled);
83 1         5 return;
84             }
85             }
86            
87             sub _parse {
88 19     19   28 my $self = shift;
89            
90 19         40 my $script = $self->{SCRIPT};
91            
92             # Look for first call to WriteMakefile function. Key should be there.
93            
94 19 100       51 if ($self->{INSTALLER} == TYPE_MAKEMAKER) {
    50          
    50          
95            
96 17         40 my $key_start = index $script, 'WriteMakefile';
97 17 50       53 if ($key_start < 0) {
98 0 0       0 carp "Error: cannot find call to WriteMakefile",
99             if (warnings::enabled);
100 0         0 return;
101             }
102            
103 17         27 $key_start = index $script, 'PREREQ_PM', $key_start;
104 17 100       31 if ($key_start < 0) {
105             # if no PREREQ_PM, we assume that there are no prereqs
106 1         3 return { };
107             } else {
108            
109 16         28 my $block_start = index $script, '{', $key_start;
110 16 100       34 if ($block_start < $key_start) {
111 2 50       37 carp "Error: cannot find left bracket after PREREQ_PM",
112             if (warnings::enabled);
113 2         681 return;
114             }
115            
116             # check that operator between PREREQ_PM and hash reference is valid
117             {
118 14         17 my $op = substr($script, $key_start, $block_start-$key_start);
  14         36  
119 14 100       119 unless ($op =~ /^[\'\"]?PREREQ_PM[\'\"]?\s*(=>|\,)\s*$/) {
120 1 50       47 carp "Error: unexpected syntax found", if (warnings::enabled);
121 1         375 return;
122             }
123             }
124            
125 13         58 my $prereq_pm = extract_bracketed(substr($script, $block_start), '{}' );
126 13 100       3401 unless ($prereq_pm) {
127 1 50       46 carp "Error: unable to extract prerequisites: no balanced brackets",
128             if (warnings::enabled);
129 1         428 return;
130             }
131            
132             # Surround bareword module names with quotes so that eval works
133             # properly. This regex will not work for code that is specified
134             # as "{qw( module 0 )}"
135            
136 12         109 $prereq_pm =~ s/([\,\s\{])(\w+)(::\w+)+\s*(=>|\,|\'?\d)/$1 '$2$3' $4/g;
137            
138 12         25 $self->{_PREREQ_PM} = $prereq_pm;
139            
140 12 100       36 if ($prereq_pm =~ /[\&\$\@\%\*]/) {
141 1 50       20 carp "Warning: possible variable references",
142             if (warnings::enabled);
143             }
144            
145 12         348 my $hashref;
146 12         862 eval "\$hashref = $prereq_pm;";
147 12         43 return _cleanup($hashref);
148             }
149             }
150             elsif ($self->{INSTALLER} == TYPE_MODULEBUILD) {
151 0         0 croak "Unsupported type";
152 0         0 return;
153             }
154             elsif ($self->{INSTALLER} == TYPE_MODULEINSTALL) {
155            
156 2         4 my $hashref = { };
157            
158 2         4 my $index = 0;
159 2         13 while (($index = index($script, 'requires', $index)) >= 0) {
160 4         5 my $reqstr;
161 4         12 my $start = index($script, '(', $index+1);
162 4 50       8 if ($start > $index) {
163 4         19 $reqstr = extract_bracketed(substr($script, $start), '()' );
164 4 50       721 if ($reqstr) {
165 4         27 my ($module, $comma, $version) =
166             split /(,|=>)/, substr($reqstr,1,-1);
167            
168 4 100       206 $hashref->{eval $module} =
169             ((defined $version) ? (eval $version) : 0);
170             }
171             else {
172 0         0 return;
173             }
174             }
175             else {
176 0         0 return;
177             }
178 4         21 $index = $index+1;
179             }
180            
181 2         7 return _cleanup($hashref);
182             }
183             else {
184 0         0 croak "Unsupported type";
185 0         0 return;
186             }
187             }
188            
189             sub install_type {
190 13     13 1 5335 my $self = shift;
191 13 50       35 if (@_) {
192 0 0       0 carp "Exra arguments ignored",
193             if (warnings::enabled);
194             }
195 13 100       39 if ($self->{INSTALLER} == TYPE_MAKEMAKER) {
    50          
    0          
196 11         49 return 'ExtUtils::MakeMaker';
197             } elsif ($self->{INSTALLER} == TYPE_MODULEINSTALL) {
198 2         10 return 'Module::Install';
199             } elsif ($self->{INSTALLER} == TYPE_MODULEBUILD) {
200 0           return 'Module::Build';
201             } else {
202 0           return;
203             }
204             }
205            
206            
207             1;
208             __END__