File Coverage

blib/lib/Module/Build/Convert.pm
Criterion Covered Total %
statement 192 880 21.8
branch 33 352 9.3
condition 25 226 11.0
subroutine 33 85 38.8
pod 2 2 100.0
total 285 1545 18.4


line stmt bran cond sub pod time code
1             package Module::Build::Convert;
2              
3 3     3   37376 use 5.005;
  3         10  
4 3     3   15 use strict;
  3         5  
  3         56  
5 3     3   12 use warnings;
  3         7  
  3         67  
6              
7 3     3   13 use Carp ();
  3         5  
  3         33  
8 3     3   11 use Cwd ();
  3         5  
  3         34  
9 3     3   1419 use Data::Dumper ();
  3         18036  
  3         61  
10 3     3   1822 use ExtUtils::MakeMaker ();
  3         253231  
  3         74  
11 3     3   21 use File::Basename ();
  3         7  
  3         34  
12 3     3   1249 use File::HomeDir ();
  3         11918  
  3         60  
13 3     3   1233 use File::Slurp ();
  3         30303  
  3         61  
14 3     3   18 use File::Spec ();
  3         7  
  3         36  
15 3     3   1132 use IO::File ();
  3         18809  
  3         60  
16 3     3   1303 use IO::Prompt ();
  3         28135  
  3         72  
17 3     3   1438 use PPI ();
  3         280016  
  3         73  
18 3     3   1637 use Text::Balanced ();
  3         21293  
  3         122  
19              
20             our $VERSION = '0.48_02';
21              
22 3     3   18 use constant LEADCHAR => '* ';
  3         6  
  3         1540  
23              
24             sub new {
25 1     1 1 12 my ($self, %params) = @_;
26 1   33     6 my $class = ref($self) || $self;
27              
28             my $obj = bless { Config => { Path => $params{Path} || '',
29             Makefile_PL => $params{Makefile_PL} || 'Makefile.PL',
30             Build_PL => $params{Build_PL} || 'Build.PL',
31             MANIFEST => $params{MANIFEST} || 'MANIFEST',
32             RC => $params{RC} || '.make2buildrc',
33             Dont_Overwrite_Auto => $params{Dont_Overwrite_Auto} || 1,
34             Create_RC => $params{Create_RC} || 0,
35             Parse_PPI => $params{Parse_PPI} || 0,
36             Exec_Makefile => $params{Exec_Makefile} || 0,
37             Verbose => $params{Verbose} || 0,
38             Debug => $params{Debug} || 0,
39             Process_Code => $params{Process_Code} || 0,
40             Use_Native_Order => $params{Use_Native_Order} || 0,
41             Len_Indent => $params{Len_Indent} || 3,
42             DD_Indent => $params{DD_Indent} || 2,
43 1   50     64 DD_Sortkeys => $params{DD_Sortkeys} || 1 }}, $class;
      50        
      50        
      50        
      50        
      50        
      50        
      50        
      50        
      50        
      50        
      50        
      50        
      50        
      50        
      50        
44              
45 1         5 $obj->{Config}{RC} = File::Spec->catfile(File::HomeDir::home(), $obj->{Config}{RC});
46              
47             # Save length of filename for creating underlined title in output
48 1         60 $obj->{Config}{Build_PL_Length} = length($obj->{Config}{Build_PL});
49              
50 1         3 return $obj;
51             }
52              
53             sub convert {
54 0     0 1 0 my $self = shift;
55              
56 0 0 0     0 unless ($self->{Config}{reinit} || @{$self->{dirs}||[]}) {
  0 0       0  
57 0 0       0 if ($self->{Config}{Path}) {
58 0 0       0 if (-f $self->{Config}{Path}) {
59 0         0 my ($basename, $dirname) = File::Basename::fileparse($self->{Config}{Path});
60 0         0 $self->{Config}{Makefile_PL} = $basename;
61 0         0 $self->{Config}{Path} = $dirname;
62             }
63              
64 0 0       0 opendir(my $dh, $self->{Config}{Path}) or die "Can't open $self->{Config}{Path}\n";
65 0         0 @{$self->{dirs}} = grep { /[\w\-]+[\d\.]+/
  0         0  
66 0 0       0 and -d File::Spec->catfile($self->{Config}{Path}, $_) } sort readdir $dh;
67              
68 0 0       0 unless (@{$self->{dirs}}) {
  0         0  
69 0         0 unshift @{$self->{dirs}}, $self->{Config}{Path};
  0         0  
70 0         0 $self->{have_single_dir} = 1;
71             }
72             } else {
73 0         0 unshift @{$self->{dirs}}, '.';
  0         0  
74 0         0 $self->{have_single_dir} = 1;
75             }
76             }
77              
78 0         0 my $Makefile_PL = File::Basename::basename($self->{Config}{Makefile_PL});
79 0         0 my $Build_PL = File::Basename::basename($self->{Config}{Build_PL});
80 0         0 my $MANIFEST = File::Basename::basename($self->{Config}{MANIFEST});
81              
82 0 0       0 unshift @{$self->{dirs}}, $self->{current_dir} if $self->{Config}{reinit};
  0         0  
83              
84 0 0       0 $self->{show_summary} = 1 if @{$self->{dirs}} > 1;
  0         0  
85              
86 0         0 while (my $dir = shift @{$self->{dirs}}) {
  0         0  
87 0         0 $self->{current_dir} = $dir;
88              
89 0         0 %{$self->{make_args}} = ();
  0         0  
90              
91 0 0       0 unless ($self->{have_single_dir}) {
92 0         0 local $" = "\n";
93 0 0       0 $self->_do_verbose(<{Config}{reinit};
94             Remaining dists:
95             ----------------
96             $dir
97 0         0 @{$self->{dirs}}
98              
99             TITLE
100             }
101              
102 0 0       0 $dir = File::Spec->catfile($self->{Config}{Path}, $dir) if !$self->{have_single_dir};
103 0         0 $self->{Config}{Makefile_PL} = File::Spec->catfile($dir, $Makefile_PL);
104 0         0 $self->{Config}{Build_PL} = File::Spec->catfile($dir, $Build_PL);
105 0         0 $self->{Config}{MANIFEST} = File::Spec->catfile($dir, $MANIFEST);
106              
107 0 0       0 unless ($self->{Config}{reinit}) {
108 3     3   23 no warnings 'uninitialized';
  3         11  
  3         3768  
109              
110 0         0 $self->_do_verbose(LEADCHAR."Converting $self->{Config}{Makefile_PL} -> $self->{Config}{Build_PL}\n");
111              
112 0         0 my $skip_msg = LEADCHAR."Skipping $self->{Config}{Path}\n";
113 0 0       0 $skip_msg .= "\n" if @{$self->{dirs}};
  0         0  
114              
115 0 0       0 $self->_create_rcfile if $self->{Config}{Create_RC};
116              
117 0 0 0     0 if (!$self->_exists_overwrite || !$self->_makefile_ok) {
118 0         0 $self->_do_verbose($skip_msg);
119 0         0 next;
120             }
121              
122 0         0 $self->_get_data;
123             }
124              
125 0         0 $self->_extract_args;
126 0         0 $self->_register_summary;
127 0         0 $self->_convert;
128 0         0 $self->_dump;
129 0         0 $self->_write;
130 0 0       0 $self->_add_to_manifest if -e $self->{Config}{MANIFEST};
131             }
132              
133 0 0       0 $self->_show_summary if $self->{show_summary};
134             }
135              
136             sub _exists_overwrite {
137 0     0   0 my $self = shift;
138              
139 0 0       0 if (-e $self->{Config}{Build_PL}) {
140             print "$self->{current_dir}:\n"
141 0 0 0     0 if $self->{show_summary} && !$self->{Config}{Verbose};
142              
143 0 0       0 print "\n" if $self->{Config}{Verbose};
144 0         0 print 'A Build.PL exists already';
145              
146 0 0       0 if ($self->{Config}{Dont_Overwrite_Auto}) {
147 0         0 print ".\n";
148 0         0 my $input_ok = IO::Prompt::prompt -yn, 'Shall I overwrite it? ';
149              
150 0 0       0 if (!$input_ok) {
151 0         0 print "Skipped...\n";
152 0 0       0 print "\n" if $self->{Config}{Verbose};
153 0         0 push @{$self->{summary}{skipped}}, $self->{current_dir};
  0         0  
154 0         0 return 0;
155             } else {
156 0 0       0 print "\n" if $self->{Config}{Verbose};
157             }
158             } else {
159 0         0 print ", continuing...\n";
160             }
161             }
162              
163 0         0 return 1;
164             }
165              
166             sub _create_rcfile {
167 0     0   0 my $self = shift;
168              
169 0         0 my $rcfile = $self->{Config}{RC};
170              
171 0 0 0     0 if (-e $rcfile && !-z $rcfile && File::Slurp::read_file($rcfile) =~ /\w+/) {
      0        
172 0         0 die "$rcfile exists\n";
173             } else {
174 0         0 my $data = $self->_parse_data('create_rc');
175 0 0       0 my $fh = IO::File->new($rcfile, '>') or die "Can't open $rcfile: $!\n";
176 0         0 print {$fh} $data;
  0         0  
177 0         0 $fh->close;
178 0         0 print LEADCHAR."Created $rcfile\n";
179 0         0 exit;
180             }
181             }
182              
183             sub _makefile_ok {
184 0     0   0 my $self = shift;
185              
186 0         0 my $makefile;
187              
188 0 0       0 if (-e $self->{Config}{Makefile_PL}) {
189 0         0 $makefile = File::Slurp::read_file($self->{Config}{Makefile_PL});
190             } else {
191             die 'No ', File::Basename::basename($self->{Config}{Makefile_PL}), ' found at ',
192             $self->{Config}{Path}
193             ? File::Basename::dirname($self->{Config}{Makefile_PL})
194 0 0       0 : Cwd::cwd(), "\n";
195             }
196              
197 0         0 my $max_failures = 2;
198 0         0 my ($failed, @failures);
199              
200 0 0       0 if ($makefile =~ /use\s+inc::Module::Install/) {
201 0         0 push @failures, "Unsuitable Makefile: Module::Install being used";
202 0         0 $failed++;
203             }
204              
205 0 0       0 unless ($makefile =~ /WriteMakefile\s*\(/s) {
206 0         0 push @failures, "Unsuitable Makefile: doesn't consist of WriteMakefile()";
207 0         0 $failed++;
208             }
209              
210 0 0 0     0 if (!$failed && $makefile =~ /WriteMakefile\(\s*%\w+.*\s*\)/s && !$self->{Config}{Exec_Makefile}) {
      0        
211 0         0 $self->_do_verbose(LEADCHAR."Indirect arguments to WriteMakefile() via hash detected, setting executing mode\n");
212 0         0 $self->{Config}{Exec_Makefile} = 1;
213             }
214              
215 0 0       0 if ($failed) {
216 0         0 my ($i, $output);
217              
218 0 0 0     0 $output .= "\n" if $self->{Config}{Verbose} && @{$self->{dirs}};
  0         0  
219 0         0 $output .= join '', map { $i++; "[$i] $_\n" } @failures;
  0         0  
  0         0  
220 0         0 $output .= "$self->{current_dir}: Failed $failed/$max_failures.\n";
221 0 0 0     0 $output .= "\n" if $self->{Config}{Verbose} && @{$self->{dirs}};
  0         0  
222              
223 0         0 print $output;
224              
225 0         0 push @{$self->{summary}{failed}}, $self->{current_dir};
  0         0  
226              
227 0         0 return 0;
228             }
229              
230 0         0 return 1;
231             }
232              
233             sub _get_data {
234 1     1   63 my $self = shift;
235 1         4 my @data = $self->_parse_data;
236              
237 1         23 $self->{Data}{table} = { split /\s+/, shift @data };
238 1         8 $self->{Data}{default_args} = { split /\s+/, shift @data };
239 1         14 $self->{Data}{sort_order} = [ split /\s+/, shift @data ];
240             ($self->{Data}{begin},
241 1         4 $self->{Data}{end}) = @data;
242              
243             # allow for embedded values such as clean => { FILES => '' }
244 1         2 foreach my $arg (keys %{$self->{Data}{table}}) {
  1         6  
245 19 100       43 if (index($arg, '.') > 0) {
246 1         4 my @path = split /\./, $arg;
247 1         3 my $value = $self->{Data}{table}->{$arg};
248 1         3 my $current = $self->{Data}{table};
249 1         3 while (@path) {
250 2         5 my $key = shift @path;
251 2 100 33     17 $current->{$key} ||= @path ? {} : $value;
252 2         6 $current = $current->{$key};
253             }
254             }
255             }
256             }
257              
258             sub _parse_data {
259 1     1   2 my $self = shift;
260 1 50 50     9 my $create_rc = 1 if (shift || 'undef') eq 'create_rc';
261              
262 1         3 my ($data, @data_parsed);
263 1         2 my $rcfile = $self->{Config}{RC};
264              
265 1 50 33     47 if (-e $rcfile && !-z $rcfile && File::Slurp::read_file($rcfile) =~ /\w+/) {
      33        
266 0         0 $data = File::Slurp::read_file($rcfile);
267             } else {
268 1         5 local $/ = '__END__';
269 1         12 $data = ;
270 1         5 chomp $data;
271             }
272              
273 1 50       7 unless ($create_rc) {
274 1         2 @data_parsed = do { # # description
275 1         16 split /#\s+.*\s+?-\n/, $data; # -
276             };
277             }
278              
279 1 50       3 unless ($create_rc) {
280             # superfluosity
281 1         2 shift @data_parsed;
282 1         3 chomp $data_parsed[-1];
283              
284 1         10 foreach my $line (split /\n/, $data_parsed[0]) {
285 19 50       36 next unless $line;
286              
287 19 50       40 if ($line =~ /^#/) {
288 0         0 my ($arg) = split /\s+/, $line;
289 0         0 $self->{disabled}{substr($arg, 1)} = 1;
290             }
291             }
292              
293 1         4 @data_parsed = map { 1 while s/^#.*?\n(.*)$/$1/gs; $_ } @data_parsed;
  5         38  
  5         10  
294             }
295              
296 1 50       6 return $create_rc ? $data : @data_parsed;
297             }
298              
299             sub _extract_args {
300 0     0   0 my $self = shift;
301              
302 0 0       0 if ($self->{Config}{Exec_Makefile}) {
303 0         0 $self->_do_verbose(LEADCHAR."Executing $self->{Config}{Makefile_PL}\n");
304 0         0 $self->_run_makefile;
305             } else {
306 0 0       0 if ($self->{Config}{Parse_PPI}) {
307 0         0 $self->_parse_makefile_ppi;
308             } else {
309 0         0 $self->_parse_makefile;
310             }
311             }
312             }
313              
314             sub _register_summary {
315 0     0   0 my $self = shift;
316              
317 0         0 push @{$self->{summary}->{succeeded}}, $self->{current_dir};
  0         0  
318              
319 0 0       0 push @{$self->{summary}{$self->{Config}{Exec_Makefile} ? 'method_execute' : 'method_parse'}},
320 0         0 $self->{current_dir};
321              
322             $self->{Config}{Exec_Makefile} =
323 0         0 $self->{Config}{reinit} = 0;
324             }
325              
326             sub _run_makefile {
327 0     0   0 my $self = shift;
328 3     3   31 no warnings 'redefine';
  3         9  
  3         538  
329              
330             *ExtUtils::MakeMaker::WriteMakefile = sub {
331 0     0   0 %{$self->{make_args}{args}} = @{$self->{make_args_arr}} = @_;
  0         0  
  0         0  
332 0         0 };
333              
334             # beware, do '' overwrites existing globals
335 0         0 $self->_save_globals;
336 0         0 do $self->{Config}{Makefile_PL};
337 0         0 $self->_restore_globals;
338             }
339              
340             sub _save_globals {
341 0     0   0 my $self = shift;
342 0         0 my @vars;
343              
344 0         0 my $makefile = File::Slurp::read_file($self->{Config}{Makefile_PL});
345 0         0 $makefile =~ s/.*WriteMakefile\(\s*?(.*?)\);.*/$1/s;
346              
347 0         0 while ($makefile =~ s/\$(\w+)//) {
348 0 0       0 push @vars, $1 if defined ${$1};
  0         0  
349             }
350              
351 3     3   20 no strict 'refs';
  3         7  
  3         210  
352 0         0 foreach my $var (@vars) {
353 0         0 ${__PACKAGE__.'::globals'}{$var} = ${$var};
  0         0  
  0         0  
354             }
355             }
356              
357             sub _restore_globals {
358 0     0   0 my $self = shift;
359 3     3   21 no strict 'refs';
  3         14  
  3         7188  
360              
361 0         0 while (my ($var, $value) = each %{__PACKAGE__.'::globals'}) {
  0         0  
362 0         0 ${__PACKAGE__.'::'.$var} = $value;
  0         0  
363             }
364 0         0 undef %{__PACKAGE__.'::globals'};
  0         0  
365             }
366              
367             sub _parse_makefile_ppi {
368 0     0   0 my $self = shift;
369              
370 0         0 $self->_parse_init;
371              
372 0         0 ($self->{parse}{makefile}, $self->{make_code}{begin}, $self->{make_code}{end}) = $self->_read_makefile;
373              
374 0         0 $self->_debug(LEADCHAR."Entering parse\n\n", 'no_wait');
375              
376 0         0 my $doc = PPI::Document->new(\$self->{parse}{makefile});
377              
378 0         0 my @elements = $doc->children;
379 0         0 my @tokens = $elements[0]->tokens;
380              
381 0         0 $self->_scrub_ternary(\@tokens);
382              
383 0         0 my ($keyword, %have, @items, %seen, $structure_ended, $type);
384              
385 0         0 for (my $i = 0; $i < @tokens; $i++) {
386             my %token = (curr => sub {
387 0     0   0 my $c = $i;
388 0         0 while (!$tokens[$c]->significant) { $c++ }
  0         0  
389 0         0 $i = $c;
390 0         0 return $tokens[$c];
391             },
392              
393             next => sub {
394 0 0   0   0 my $iter = $_[0] ? $_[0] : 1;
395 0         0 my ($c, $pos) = ($i + 1, 0);
396              
397 0         0 while ($c < @tokens) {
398 0 0       0 $pos++ if $tokens[$c]->significant;
399 0 0       0 last if $pos == $iter;
400 0         0 $c++;
401             }
402              
403 0         0 return $tokens[$c];
404             },
405              
406             last => sub {
407 0 0   0   0 my $iter = $_[0] ? $_[0] : 1;
408 0         0 my ($c, $pos) = ($i, 0);
409              
410 0 0       0 $c-- if $c >= 1;
411              
412 0         0 while ($c > 0) {
413 0 0       0 $pos++ if $tokens[$c]->significant;
414 0 0       0 last if $pos == $iter;
415 0         0 $c--;
416             }
417              
418 0         0 return $tokens[$c];
419 0         0 });
420              
421 0     0   0 my %finalize = (string => sub { $self->{parse}{makeargs}{$keyword} = join '', @items },
422 0     0   0 array => sub { $self->{parse}{makeargs}{$keyword} = [ @items ] },
423 0     0   0 hash => sub { $self->{parse}{makeargs}{$keyword} = { @items } });
  0         0  
424              
425 0 0       0 my $token = $have{code} ? $tokens[$i] : $token{curr}->();
426              
427 0 0 0     0 if ($self->_is_quotelike($token) && !$have{code} && !$have{nested_structure} && $token{last}->(1) ne '=>') {
    0 0        
      0        
      0        
428 0         0 $keyword = $token;
429 0         0 $type = 'string';
430 0         0 next;
431             } elsif ($token eq '=>' && !$have{nested_structure}) {
432 0         0 next;
433             }
434              
435 0 0 0     0 next if $structure_ended && $token eq ',';
436 0         0 $structure_ended = 0;
437              
438 0 0 0     0 if ($token->isa('PPI::Token::Structure') && !$have{code}) {
439 0 0       0 if ($token =~ /[\Q[{\E]/) {
    0          
440 0         0 $have{nested_structure}++;
441              
442 0         0 my %assoc = ('[' => 'array',
443             '{' => 'hash');
444              
445 0         0 $type = $assoc{$token};
446             } elsif ($token =~ /[\Q]}\E]/) {
447 0         0 $have{nested_structure}--;
448 0 0       0 $structure_ended = 1 unless $have{nested_structure};
449             }
450             }
451              
452 0 0 0     0 $structure_ended = 1 if $token{next}->() eq ',' && !$have{code} && !$have{nested_structure};
      0        
453 0 0 0     0 $have{code} = 1 if $token->isa('PPI::Token::Word') && $token{next}->(1) ne '=>';
454              
455 0 0       0 if ($have{code}) {
456 0 0   0   0 my $followed_by_arrow = sub { $token eq ',' && $token{next}->(2) eq '=>' };
  0         0  
457              
458 0     0   0 my %finalize = (seen => sub { $structure_ended = 1; $seen{code} = 1; $have{code} = 0 },
  0         0  
  0         0  
459 0     0   0 unseen => sub { $structure_ended = 1; $seen{code} = 0; $have{code} = 0 });
  0         0  
  0         0  
  0         0  
460              
461 0 0 0     0 if ($followed_by_arrow->()) {
    0 0        
462             ($token{next}->(1) =~ /^[\Q}]\E]$/ || !$have{nested_structure})
463             ? $finalize{seen}->()
464             : $have{nested_structure}
465 0 0 0     0 ? $finalize{unseen}->()
    0          
466             : ();
467             } elsif (($token eq ',' && $token{next}->(1) eq ']')
468             || $token{next}->(1) eq ']') {
469 0         0 $finalize{unseen}->();
470             }
471             }
472              
473 0 0 0     0 unless ($token =~ /^[\Q[]{}\E]$/ && !$have{code}) {
474 0 0       0 next if $token eq '=>';
475 0 0 0     0 next if $token eq ',' && !$have{code} && !$seen{code};
      0        
476              
477 0 0       0 if (defined $keyword) {
478 0         0 $keyword =~ s/['"]//g;
479 0 0 0     0 $token =~ s/['"]//g unless $token =~ /^['"]\s+['"]$/ || $have{code};
480              
481 0 0 0     0 if (!$have{code} && !$structure_ended) {
482 0         0 push @items, $token;
483             } else {
484 0 0 0     0 if ((@items % 2 == 1 && $type ne 'array') || !@items) {
      0        
485 0         0 push @items, $token;
486             } else {
487 0 0 0     0 $items[-1] .= $token unless $structure_ended
488             && $type eq 'string';
489             }
490             }
491             }
492             }
493              
494 0 0 0     0 if ($structure_ended && @items) {
495             # Obscure construct. Needed to 'serialize' the PPI tokens.
496 0         0 @items = map { /(.*)/; $1 } @items;
  0         0  
  0         0  
497              
498             # Sanitize code elements within a hash.
499 0 0 0     0 $items[-1] =~ s/[,\s]+$// if $type eq 'hash' && defined $items[-1];
500              
501 0         0 $finalize{$type}->();
502              
503 0         0 undef $keyword;
504              
505 0         0 $have{code} = 0;
506 0         0 @items = ();
507 0         0 %seen = ();
508              
509 0         0 $type = 'string';
510             }
511             }
512              
513 0         0 $self->_debug(LEADCHAR."Leaving parse\n\n", 'no_wait');
514              
515 0         0 %{$self->{make_args}{args}} = %{$self->{parse}{makeargs}};
  0         0  
  0         0  
516             }
517              
518             sub _is_quotelike {
519 0     0   0 my ($self, $token) = @_;
520              
521 0 0 0     0 return ($token->isa('PPI::Token::Double')
522             or $token->isa('PPI::Token::Quote::Interpolate')
523             or $token->isa('PPI::Token::Quote::Literal')
524             or $token->isa('PPI::Token::Quote::Single')
525             or $token->isa('PPI::Token::Word')) ? 1 : 0;
526             }
527              
528             sub _scrub_ternary {
529 0     0   0 my ($self, $tokens) = @_;
530              
531 0         0 my (%last, %have, %occurences);
532              
533 0         0 for (my $i = 0; $i < @$tokens; $i++) {
534 0         0 my $token = $tokens->[$i];
535              
536 0 0 0     0 $last{comma} = $i if $token eq ',' && !$have{'?'};
537              
538 0 0       0 unless ($have{ternary}) {
539 0 0       0 $occurences{subsequent}{'('}++ if $token eq '(';
540 0 0       0 $occurences{subsequent}{')'}++ if $token eq ')';
541             }
542              
543 0 0       0 $have{'?'} = 1 if $token eq '?';
544 0 0       0 $have{':'} = 1 if $token eq ':';
545              
546 0 0 0     0 $have{ternary} = 1 if $have{'?'} && $have{':'};
547              
548 0 0       0 if ($have{ternary}) {
549 0   0     0 $occurences{'('} ||= 0;
550 0   0     0 $occurences{')'} ||= 0;
551              
552 0         0 $occurences{'('} += $occurences{subsequent}{'('};
553 0         0 $occurences{')'} += $occurences{subsequent}{')'};
554              
555 0         0 $occurences{subsequent}{'('} = 0;
556 0         0 $occurences{subsequent}{')'} = 0;
557              
558 0 0       0 $occurences{'('}++ if $token eq '(';
559 0 0       0 $occurences{')'}++ if $token eq ')';
560              
561 0 0 0     0 $have{parentheses} = 1 if $occurences{'('} || $occurences{')'};
562 0 0       0 $have{comma} = 1 if $token eq ',';
563              
564 0 0 0     0 if ($occurences{'('} == $occurences{')'} && $have{parentheses} && $have{comma}) {
      0        
565 0         0 $i++ while $tokens->[$i] ne ',';
566 0         0 splice(@$tokens, $last{comma}, $i-$last{comma});
567              
568 0         0 @have{qw(? : comma parentheses ternary)} = (0,0,0,0,0);
569 0         0 @occurences{qw{( )}} = (0,0);
570              
571 0         0 $i = 0; redo;
  0         0  
572             }
573             }
574             }
575             }
576              
577             sub _parse_makefile {
578 0     0   0 my $self = shift;
579              
580 0         0 $self->_parse_init;
581              
582 0         0 ($self->{parse}{makefile}, $self->{make_code}{begin}, $self->{make_code}{end}) = $self->_read_makefile;
583 0         0 my ($found_string, $found_array, $found_hash) = $self->_parse_regexps;
584              
585 0         0 $self->_debug(LEADCHAR."Entering parse\n\n", 'no_wait');
586              
587 0         0 while ($self->{parse}{makefile}) {
588             $self->{parse}{makefile} .= "\n"
589 0 0       0 unless $self->{parse}{makefile} =~ /\n$/s;
590              
591             # process string
592 0 0       0 if ($self->{parse}{makefile} =~ s/$found_string//) {
    0          
    0          
593 0         0 $self->_parse_process_string($1,$2,$3);
594 0         0 $self->_parse_register_comment;
595 0         0 $self->_debug($self->_debug_string_text);
596             # process array
597             } elsif ($self->{parse}{makefile} =~ s/$found_array//s) {
598 0         0 $self->_parse_process_array($1,$2,$3);
599 0         0 $self->_parse_register_comment;
600 0         0 $self->_debug($self->_debug_array_text);
601             # process hash
602             } elsif ($self->{parse}{makefile} =~ s/$found_hash//s) {
603 0         0 $self->_parse_process_hash($1,$2,$3);
604 0         0 $self->_parse_register_comment;
605 0         0 $self->_debug($self->_debug_hash_text);
606             # process "code"
607             } else {
608 0         0 chomp $self->{parse}{makefile};
609              
610 0         0 $self->_parse_process_code;
611 0         0 $self->_parse_catch_trapped_loop;
612              
613 0 0       0 if ($self->{Config}{Process_Code}) {
614 0         0 $self->_parse_substitute_makeargs;
615 0         0 $self->_parse_append_makecode;
616 0         0 $self->_debug($self->_debug_code_text);
617             }
618             }
619              
620             $self->{parse}{makefile} = ''
621 0 0       0 unless $self->{parse}{makefile} =~ /\w/;
622             }
623              
624 0         0 $self->_debug(LEADCHAR."Leaving parse\n\n", 'no_wait');
625              
626 0         0 %{$self->{make_args}{args}} = %{$self->{parse}{makeargs}};
  0         0  
  0         0  
627             }
628              
629             sub _parse_init {
630 0     0   0 my $self = shift;
631              
632 0         0 %{$self->{make_code}} = ();
  0         0  
633 0         0 %{$self->{parse}} = ();
  0         0  
634             }
635              
636             sub _parse_regexps {
637 0     0   0 my $self = shift;
638              
639 0         0 my $found_string = qr/^
640             \s*
641             ['"]? (\w+) ['"]?
642             \s* => \s* (?![ \{ \[ ])
643             ['"]? ([\$ \@ \% \< \> \( \) \\ \/ \- \: \. \w]+.*?) ['"]?
644             ,? ([^\n]+ \# \s+ \w+ .*?)? \n
645             /sx;
646 0         0 my $found_array = qr/^
647             \s*
648             ['"]? (\w+) ['"]?
649             \s* => \s*
650             \[ \s* (.*?) \s* \]
651             ,? ([^\n]+ \# \s+ \w+ .*?)? \n
652             /sx;
653 0         0 my $found_hash = qr/^
654             \s*
655             ['"]? (\w+) ['"]?
656             \s* => \s*
657             \{ \s* (.*?) \s*? \}
658             ,? ([^\n]+ \# \s+ \w+ .*?)? \n
659             /sx;
660              
661 0         0 return ($found_string, $found_array, $found_hash);
662             }
663              
664             sub _parse_process_string {
665 0     0   0 my ($self, $arg, $value, $comment) = @_;
666              
667 0   0     0 $value ||= '';
668 0   0     0 $comment ||= '';
669              
670 0         0 $value =~ s/^['"]//;
671 0         0 $value =~ s/['"]$//;
672              
673 0         0 $self->{parse}{makeargs}{$arg} = $value;
674 0         0 push @{$self->{parse}{histargs}}, $arg;
  0         0  
675              
676 0         0 $self->{parse}{arg} = $arg;
677 0         0 $self->{parse}{value} = $value;
678 0         0 $self->{parse}{comment} = $comment;
679             }
680              
681             sub _parse_process_array {
682 0     0   0 my ($self, $arg, $values, $comment) = @_;
683              
684 0   0     0 $values ||= '';
685 0   0     0 $comment ||= '';
686              
687 0         0 $self->{parse}{makeargs}{$arg} = [ map { tr/['",]//d; $_ } split /,\s*/, $values ];
  0         0  
  0         0  
688 0         0 push @{$self->{parse}{histargs}}, $arg;
  0         0  
689              
690 0         0 $self->{parse}{arg} = $arg;
691             $self->{parse}{values} = $self->{parse}{makeargs}{$arg},
692 0         0 $self->{parse}{comment} = $comment;
693             }
694              
695              
696             sub _parse_process_hash {
697 0     0   0 my ($self, $arg, $values, $comment) = @_;
698              
699 0   0     0 $values ||= '';
700 0   0     0 $comment ||= '';
701              
702 0         0 my @values_debug = split /,\s*/, $values;
703 0         0 my @values;
704              
705 0         0 foreach my $value (@values_debug) {
706 0         0 push @values, map { tr/['",]//d; $_ } split /\s*=>\s*/, $value;
  0         0  
  0         0  
707             }
708              
709 0         0 @values_debug = map { "$_\n " } @values_debug;
  0         0  
710              
711 0         0 $self->{parse}{makeargs}{$arg} = { @values };
712 0         0 push @{$self->{parse}{histargs}}, $arg;
  0         0  
713              
714 0         0 $self->{parse}{arg} = $arg;
715             $self->{parse}{values} = \@values_debug,
716 0         0 $self->{parse}{comment} = $comment;
717             }
718              
719             sub _parse_process_code {
720 0     0   0 my $self = shift;
721              
722 0         0 my ($debug_desc, $retval);
723              
724 0         0 my @code = Text::Balanced::extract_codeblock($self->{parse}{makefile}, '()');
725 0         0 my @variable = Text::Balanced::extract_variable($self->{parse}{makefile});
726              
727             # [0] extracted
728             # [1] remainder
729              
730 0 0       0 if ($code[0]) {
    0          
    0          
731 0         0 $code[0] =~ s/^\s*\(\s*//s;
732 0         0 $code[0] =~ s/\s*\)\s*$//s;
733              
734 0         0 $code[0] =~ s/\s*=>\s*/\ =>\ /gs;
735 0         0 $code[1] =~ s/^\s*,//;
736              
737 0         0 $self->{parse}{makefile} = $code[1];
738 0         0 $retval = $code[0];
739              
740 0         0 $debug_desc = 'code';
741             } elsif ($variable[0]) {
742 0         0 $self->{parse}{makefile} = $variable[1];
743 0         0 $retval = $variable[0];
744              
745 0         0 $debug_desc = 'variable';
746             } elsif ($self->{parse}{makefile} =~ /\#/) {
747 0         0 my $comment;
748              
749             $self->{parse}{makefile} .= "\n"
750 0 0       0 unless $self->{parse}{makefile} =~ /\n$/s;
751              
752 0         0 while ($self->{parse}{makefile} =~ /\G(\s*?\#.*?\n)/cgs) {
753 0         0 $comment .= $1;
754             }
755              
756 0   0     0 $comment ||= '';
757              
758 0         0 my $quoted_comment = quotemeta $comment;
759 0         0 $self->{parse}{makefile} =~ s/$quoted_comment//s;
760              
761 0         0 my @comment;
762              
763 0         0 @comment = split /\n/, $comment;
764 0         0 @comment = grep { /\#/ } @comment;
  0         0  
765              
766 0         0 foreach $comment (@comment) {
767 0         0 $comment =~ s/^\s*?(\#.*)$/$1/gm;
768 0         0 chomp $comment;
769             }
770              
771 0         0 $retval = \@comment;
772 0         0 $debug_desc = 'comment';
773             } else {
774 0         0 $retval = '';
775 0         0 $debug_desc = 'unclassified';
776             }
777              
778 0         0 $self->{parse}{debug_desc} = $debug_desc;
779 0         0 $self->{parse}{makecode} = $retval;
780             }
781              
782             sub _parse_catch_trapped_loop {
783 0     0   0 my $self = shift;
784              
785 3     3   22 no warnings 'uninitialized';
  3         10  
  3         3419  
786              
787             $self->{parse}{trapped_loop}{$self->{parse}{makecode}}++
788 0 0       0 if $self->{parse}{makecode} eq $self->{makecode_prev};
789              
790 0 0       0 if ($self->{parse}{trapped_loop}{$self->{parse}{makecode}} > 1) {
791 0         0 $self->{Config}{Exec_Makefile} = 1;
792 0         0 $self->{Config}{reinit} = 1;
793 0         0 $self->convert;
794 0         0 exit;
795             }
796              
797 0         0 $self->{makecode_prev} = $self->{parse}{makecode};
798             }
799              
800             sub _parse_substitute_makeargs {
801 0     0   0 my $self = shift;
802              
803 0   0     0 $self->{parse}{makecode} ||= '';
804              
805 0         0 foreach my $make (keys %{$self->{Data}{table}}) {
  0         0  
806 0 0       0 if ($self->{parse}{makecode} =~ /\b$make\b/s) {
807 0         0 $self->{parse}{makecode} =~ s/$make/$self->{Data}{table}{$make}/;
808             }
809             }
810             }
811              
812             sub _parse_append_makecode {
813 0     0   0 my $self = shift;
814              
815 0 0       0 unless (@{$self->{parse}{histargs}||[]}) {
  0 0       0  
816 0         0 push @{$self->{make_code}{args}{begin}}, $self->{parse}{makecode};
  0         0  
817             } else {
818 0         0 pop @{$self->{parse}{histargs}}
819 0         0 until $self->{Data}{table}{$self->{parse}{histargs}->[-1]};
820              
821 0         0 push @{$self->{make_code}{args}{$self->{Data}{table}{$self->{parse}{histargs}->[-1]}}},
822 0         0 $self->{parse}{makecode};
823             }
824             }
825              
826             sub _parse_register_comment {
827 0     0   0 my $self = shift;
828              
829 0         0 my $arg = $self->{parse}{arg};
830 0         0 my $comment = $self->{parse}{comment};
831              
832 0 0 0     0 if (defined($comment) && defined($self->{Data}{table}{$arg})) {
833 0         0 $self->{make_comments}{$self->{Data}{table}{$arg}} = $comment;
834             }
835             }
836              
837             sub _debug_string_text {
838 0     0   0 my $self = shift;
839              
840 0         0 my $output = <
841             Found string ''
842             +++++++++++++++
843             \$arg: $self->{parse}{arg}
844             \$value: $self->{parse}{value}
845             \$comment: $self->{parse}{comment}
846             \$remaining args:
847             $self->{parse}{makefile}
848              
849             EOT
850 0         0 return $output;
851             }
852              
853             sub _debug_array_text {
854 0     0   0 my $self = shift;
855              
856 0         0 my @values = @{$self->{parse}{values}};
  0         0  
857              
858 0         0 my $output = <
859             Found array []
860             ++++++++++++++
861             \$arg: $self->{parse}{arg}
862             \$values: @values
863             \$comment: $self->{parse}{comment}
864             \$remaining args:
865             $self->{parse}{makefile}
866              
867             EOT
868 0         0 return $output;
869             }
870              
871             sub _debug_hash_text {
872 0     0   0 my $self = shift;
873              
874 0         0 my $output = <
875             Found hash {}
876             +++++++++++++
877             \$key: $self->{parse}{arg}
878 0         0 \$values: @{$self->{parse}{values}}
879             \$comment: $self->{parse}{comment}
880             \$remaining args:
881             $self->{parse}{makefile}
882             EOT
883 0         0 return $output;
884             }
885              
886             sub _debug_code_text {
887 0     0   0 my $self = shift;
888              
889 0         0 my @args;
890              
891 0 0       0 if (ref $self->{parse}{makecode} eq 'ARRAY') {
892 0         0 push @args, @{$self->{parse}{makecode}};
  0         0  
893             } else {
894 0         0 push @args, $self->{parse}{makecode};
895             }
896              
897 0 0       0 @args = map { "\n$_" } @args if @args > 1;
  0         0  
898              
899 0         0 my $output = <
900             Found code &
901             ++++++++++++
902             $self->{parse}{debug_desc}: @args
903             remaining args:
904             $self->{parse}{makefile}
905              
906             EOT
907 0         0 return $output;
908             }
909              
910             sub _read_makefile {
911 0     0   0 my $self = shift;
912              
913 0         0 my $makefile = File::Slurp::read_file($self->{Config}{Makefile_PL});
914 0         0 $makefile =~ s/^(.*?)\&?WriteMakefile\s*?\(\s*(.*?)\s*\)\s*?;(.*)$/$2/s;
915              
916 0         0 my $makecode_begin = $1;
917 0         0 my $makecode_end = $3;
918 0         0 $makecode_begin =~ s/\s*([\#\w]+.*)\s*/$1/s;
919 0         0 $makecode_end =~ s/\s*([\#\w]+.*)\s*/$1/s;
920              
921 0         0 return ($makefile, $makecode_begin, $makecode_end);
922             }
923              
924             sub _convert {
925 1     1   7 my $self = shift;
926              
927 1         4 $self->_insert_args;
928              
929 1         2 foreach my $arg (keys %{$self->{make_args}{args}}) {
  1         5  
930 19 50       41 if ($self->{disabled}{$arg}) {
931 0         0 $self->_do_verbose(LEADCHAR."$arg disabled, skipping\n");
932 0         0 next;
933             }
934 19 50       40 unless ($self->{Data}{table}->{$arg}) {
935 0         0 $self->_do_verbose(LEADCHAR."$arg unknown, skipping\n");
936 0         0 next;
937             }
938 19 100       50 if (ref $self->{make_args}{args}{$arg} eq 'HASH') {
    100          
    50          
939 5 50       12 if (ref $self->{Data}{table}->{$arg} eq 'HASH') {
940             # embedded structure
941 0         0 my @iterators = ();
942 0         0 my $current = $self->{Data}{table}->{$arg};
943 0         0 my $value = $self->{make_args}{args}{$arg};
944 0         0 push @iterators, _iterator($current, $value, keys %$current);
945 0         0 while (@iterators) {
946 0         0 my $iterator = shift @iterators;
947 0         0 while (($current, $value) = $iterator->()) {
948 0 0       0 if (ref $current eq 'HASH') {
949 0         0 push @iterators, _iterator($current, $value, keys %$current);
950             } else {
951 0 0       0 if (substr($current, 0, 1) eq '@') {
952 0         0 my $attr = substr($current, 1);
953 0 0       0 if (ref $value eq 'ARRAY') {
954 0         0 push @{$self->{build_args}}, { $attr => $value };
  0         0  
955             } else {
956 0         0 push @{$self->{build_args}}, { $attr => [ split ' ', $value ] };
  0         0  
957             }
958             } else {
959 0         0 push @{$self->{build_args}}, { $current => $value };
  0         0  
960             }
961             }
962             }
963             }
964             } else {
965             # flat structure
966 5         7 my %tmphash;
967 5         15 %{$tmphash{$self->{Data}{table}->{$arg}}} =
968 5         7 map { $_ => $self->{make_args}{args}{$arg}{$_} } keys %{$self->{make_args}{args}{$arg}};
  0         0  
  5         10  
969 5         8 push @{$self->{build_args}}, \%tmphash;
  5         10  
970             }
971             } elsif (ref $self->{make_args}{args}{$arg} eq 'ARRAY') {
972 1         1 push @{$self->{build_args}}, { $self->{Data}{table}->{$arg} => $self->{make_args}{args}{$arg} };
  1         3  
973             } elsif (ref $self->{make_args}{args}{$arg} eq '') {
974 13         21 push @{$self->{build_args}}, { $self->{Data}{table}->{$arg} => $self->{make_args}{args}{$arg} };
  13         42  
975             } else { # unknown type
976 0         0 warn "$arg - unknown type of argument\n";
977             }
978             }
979              
980 1 50       3 $self->_sort_args if @{$self->{Data}{sort_order}};
  1         6  
981             }
982              
983             sub _insert_args {
984 1     1   3 my ($self, $make) = @_;
985              
986 1         2 my @insert_args;
987 1         2 my %build = map { $self->{Data}{table}{$_} => $_ } keys %{$self->{Data}{table}};
  20         44  
  1         4  
988              
989 1         4 while (my ($arg, $value) = each %{$self->{Data}{default_args}}) {
  4         15  
990 3     3   22 no warnings 'uninitialized';
  3         9  
  3         614  
991              
992 3 100       11 if (exists $self->{make_args}{args}{$build{$arg}}) {
993 1         6 $self->_do_verbose(LEADCHAR."Overriding default \'$arg => $value\'\n");
994 1         2 next;
995             }
996              
997 2 50       6 $value = {} if $value eq 'HASH';
998 2 50       5 $value = [] if $value eq 'ARRAY';
999 2 50 33     8 $value = '' if $value eq 'SCALAR' && $value !~ /\d+/;
1000              
1001 2         6 push @insert_args, { $arg => $value };
1002             }
1003              
1004 1         13 @{$self->{build_args}} = @insert_args;
  1         6  
1005             }
1006              
1007             sub _iterator {
1008 0     0   0 my ($build, $make) = (shift, shift);
1009 0         0 my @queue = @_;
1010              
1011             return sub {
1012 0   0 0   0 my $key = shift @queue || return;
1013 0         0 return $build->{$key}, $make->{$key};
1014             }
1015 0         0 }
1016              
1017             sub _sort_args {
1018 1     1   2 my $self = shift;
1019              
1020 1         2 my %native_sortorder;
1021              
1022 1 50       4 if ($self->{Config}{Use_Native_Order}) {
1023 3     3   20 no warnings 'uninitialized';
  3         6  
  3         1252  
1024              
1025             # Mapping an incremental value to the arguments (keys) in the
1026             # order they appear.
1027 0         0 for (my ($i,$s) = 0; $s < @{$self->{make_args_arr}}; $s++) {
  0         0  
1028             # Skipping values
1029 0 0       0 next unless $s % 2 == 0;
1030             # Populating table with according M::B arguments and counter
1031             $native_sortorder{$self->{Data}{table}{$self->{make_args_arr}[$s]}} = $i
1032 0 0       0 if exists $self->{Data}{table}{$self->{make_args_arr}[$s]};
1033 0         0 $i++;
1034             }
1035             }
1036              
1037 1         2 my %sortorder;
1038             {
1039 1         1 my %have_args = map { keys %$_ => 1 } @{$self->{build_args}};
  1         2  
  21         46  
  1         3  
1040             # Filter sort items, that we didn't receive as args,
1041             # and map the rest to according array indexes.
1042 1         4 my $i = 0;
1043 1 50       4 if ($self->{Config}{Use_Native_Order}) {
1044 0         0 my %slot;
1045              
1046 0         0 foreach my $arg (grep $have_args{$_}, @{$self->{Data}{sort_order}}) {
  0         0  
1047             # Building sorting table for existing MakeMaker arguments
1048 0 0       0 if ($native_sortorder{$arg}) {
1049 0         0 $sortorder{$arg} = $native_sortorder{$arg};
1050 0         0 $slot{$native_sortorder{$arg}} = 1;
1051             # Inject default arguments at free indexes
1052             } else {
1053 0         0 $i++ while $slot{$i};
1054 0         0 $sortorder{$arg} = $i++;
1055             }
1056             }
1057              
1058             # Sorting sort table ascending
1059 0         0 my @args = sort { $sortorder{$a} <=> $sortorder{$b} } keys %sortorder;
  0         0  
1060 0         0 $i = 0; %sortorder = map { $_ => $i++ } @args;
  0         0  
  0         0  
1061              
1062             } else {
1063             %sortorder = map {
1064 20         40 $_ => $i++
1065 1         2 } grep $have_args{$_}, @{$self->{Data}{sort_order}};
  1         7  
1066             }
1067             }
1068              
1069 1         3 my ($is_sorted, @unsorted);
1070 1         2 do {
1071              
1072 182         247 $is_sorted = 1;
1073              
1074 182         255 SORT: for (my $i = 0; $i < @{$self->{build_args}}; $i++) {
  973         1918  
1075 972         1250 my ($arg) = keys %{$self->{build_args}[$i]};
  972         1681  
1076              
1077 972 100       1957 unless (exists $sortorder{$arg}) {
1078 1         2 push @unsorted, splice(@{$self->{build_args}}, $i, 1);
  1         2  
1079 1         2 next;
1080             }
1081              
1082 971 100       1935 if ($i != $sortorder{$arg}) {
1083 181         248 $is_sorted = 0;
1084             # Move element $i to pos $sortorder{$arg}
1085             # and the element at $sortorder{$arg} to
1086             # the end.
1087 181         266 push @{$self->{build_args}},
1088 181         262 splice(@{$self->{build_args}}, $sortorder{$arg}, 1,
1089 181         248 splice(@{$self->{build_args}}, $i, 1));
  181         324  
1090              
1091 181         424 last SORT;
1092             }
1093             }
1094             } until ($is_sorted);
1095              
1096 1         3 push @{$self->{build_args}}, @unsorted;
  1         8  
1097             }
1098              
1099             sub _dump {
1100 0     0   0 my $self = shift;
1101              
1102 0   0     0 $Data::Dumper::Indent = $self->{Config}{DD_Indent} || 2;
1103 0         0 $Data::Dumper::Quotekeys = 0;
1104 0         0 $Data::Dumper::Sortkeys = $self->{Config}{DD_Sortkeys};
1105 0         0 $Data::Dumper::Terse = 1;
1106              
1107 0         0 my $d = Data::Dumper->new(\@{$self->{build_args}});
  0         0  
1108 0         0 $self->{buildargs_dumped} = [ $d->Dump ];
1109             }
1110              
1111             sub _write {
1112 0     0   0 my $self = shift;
1113              
1114 0         0 $self->{INDENT} = ' ' x $self->{Config}{Len_Indent};
1115              
1116 3     3   22 no warnings 'once';
  3         6  
  3         513  
1117 0 0       0 my $fh = IO::File->new($self->{Config}{Build_PL}, '>')
1118             or die "Can't open $self->{Config}{Build_PL}: $!\n";
1119              
1120 0         0 my $selold = select($fh);
1121              
1122 0         0 $self->_compose_header;
1123 0         0 $self->_write_begin;
1124 0         0 $self->_write_args;
1125 0         0 $self->_write_end;
1126 0         0 $fh->close;
1127              
1128 0         0 select($selold);
1129              
1130 0         0 $self->_do_verbose("\n", LEADCHAR."Conversion done\n");
1131 0 0       0 $self->_do_verbose("\n") if !$self->{have_single_dir};
1132             }
1133              
1134             sub _compose_header {
1135 0     0   0 my $self = shift;
1136              
1137 0         0 my ($comments_header, $code_header);
1138              
1139 0         0 my $note = '# Note: this file has been initially generated by ' . __PACKAGE__ . " $VERSION";
1140 0         0 my $pragmas = "use strict;\nuse warnings;\n";
1141              
1142             # Warnings are thrown for chomp() & regular expressions when enabled
1143 3     3   17 no warnings 'uninitialized';
  3         6  
  3         4746  
1144              
1145 0 0 0     0 if (defined $self->{make_code}{begin} || defined $self->{make_code}{end}) {
1146             # Removing ExtUtils::MakeMaker dependency
1147 0         0 $self->_do_verbose(LEADCHAR."Removing ExtUtils::MakeMaker as dependency\n");
1148 0         0 $self->{make_code}{begin} =~ s/[ \t]*(?:use|require)\s+ExtUtils::MakeMaker\s*;//;
1149              
1150             # Mapping (prompt|Verbose) calls to Module::Build ones
1151 0 0       0 if ($self->{make_code}{begin} =~ /(?:prompt|Verbose)\s*\(/s) {
1152 0         0 my $regexp = qr/^(.*?=\s*)(prompt|Verbose)\s*?\(['"](.*)['"]\);$/;
1153              
1154 0         0 foreach my $var (qw(begin end)) {
1155 0         0 while ($self->{make_code}{$var} =~ /$regexp/m) {
1156 0         0 my $replace = $1 . 'Module::Build->' . $2 . '("' . $3 . '");';
1157 0         0 $self->{make_code}{$var} =~ s/$regexp/$replace/m;
1158             }
1159             }
1160             }
1161              
1162             # Removing Module::Build::Compat Note
1163 0 0       0 if ($self->{make_code}{begin} =~ /Module::Build::Compat/) {
1164 0         0 $self->_do_verbose(LEADCHAR."Removing Module::Build::Compat Note\n");
1165 0         0 $self->{make_code}{begin} =~ s/^\#.*Module::Build::Compat.*?\n//s;
1166             }
1167              
1168             # Removing customized MakeMaker subs
1169 0         0 my $has_MM_sub = qr/sub MY::/;
1170 0         0 my $MM_sub_prefix = 'MY::';
1171              
1172 0         0 foreach my $var (qw(begin end)) {
1173 0 0       0 if ($self->{make_code}{$var} =~ $has_MM_sub) {
1174 0         0 foreach my $sub (_extract_sub($self->{make_code}{$var}, $MM_sub_prefix)) {
1175 0         0 my $quoted_sub = quotemeta $sub;
1176 0         0 my ($subname) = $sub =~ /sub.*?\s+(.*?)\s*\{/;
1177              
1178 0         0 $self->{make_code}{$var} =~ s/$quoted_sub\n//;
1179 0         0 $self->_do_verbose(LEADCHAR."Removing sub: '$subname'\n");
1180             }
1181             }
1182             }
1183              
1184             # Removing strict & warnings pragmas quietly here to ensure that they'll
1185             # be inserted after an eventually appearing version requirement.
1186 0         0 $self->{make_code}{begin} =~ s/[ \t]*use\s+(?:strict|warnings)\s*;//g;
1187              
1188             # Saving the shebang (interpreter) line
1189 0         0 while ($self->{make_code}{begin} =~ s/^(\#\!?.*?\n)//) {
1190 0         0 $comments_header .= $1;
1191             }
1192 0         0 chomp $comments_header;
1193              
1194             # Grabbing use & require statements
1195 0         0 while ($self->{make_code}{begin} =~ /^(?:use|require)\s+(?:[a-z]|[\d\.\_])+?\s*;/m) {
1196 0         0 $self->{make_code}{begin} =~ s/^\n*(.*?;)//s;
1197 0         0 $code_header .= "$1\n";
1198             }
1199              
1200             # Adding strict & warnings pragmas
1201 0         0 $self->_do_verbose(LEADCHAR."Adding use strict & use warnings pragmas\n");
1202              
1203 0 0       0 if ($code_header =~ /(?:use|require)\s+\d\.[\d_]*\s*;/) {
1204 0         0 $code_header =~ s/([ \t]*(?:use|require)\s+\d\.[\d_]*\s*;\n)(.*)/$1$pragmas$2/;
1205             } else {
1206 0         0 $code_header = $pragmas . $code_header;
1207             }
1208 0         0 chomp $code_header;
1209              
1210             # Removing leading & trailing newlines
1211 0         0 1 while $self->{make_code}{begin} =~ s/^\n//;
1212 0         0 chomp $self->{make_code}{begin} while $self->{make_code}{begin} =~ /\n$/s;
1213             }
1214              
1215             # Constructing the Build.PL header
1216             $self->{Data}{begin} = $comments_header || $code_header
1217             ? ($comments_header =~ /\w/ ? "$comments_header\n" : '') . "$note\n" .
1218             ($code_header =~ /\w/ ? "\n$code_header\n\n" : "\n") .
1219             $self->{Data}{begin}
1220 0 0 0     0 : "$note\n\n" . $self->{Data}{begin};
    0          
    0          
1221             }
1222              
1223             # Albeit Text::Balanced exists, extract_tagged() and friends
1224             # were (or I?) unable to extract subs.
1225             sub _extract_sub {
1226 0     0   0 my ($text, $pattern) = @_;
1227              
1228 0         0 my ($quoted_pattern, %seen, @sub, @subs);
1229              
1230 0         0 $quoted_pattern = quotemeta $pattern;
1231              
1232 0         0 foreach my $line (split /\n/, $text) {
1233 0 0 0     0 if ($line =~ /^sub $quoted_pattern\w+/s ||
1234 0         0 $line =~ /^\{/) { $seen{begin} = 1 }
1235 0 0 0     0 if ($seen{begin} && $line =~ /^\s*}/) { $seen{end} = 1 }
  0         0  
1236              
1237 0 0 0     0 if ($seen{begin} || $seen{end}) {
1238 0         0 push @sub, $line;
1239             } else {
1240 0         0 next;
1241             }
1242              
1243 0 0       0 if ($seen{end}) {
1244 0         0 push @subs, join "\n", @sub;
1245 0         0 @sub = ();
1246 0         0 @seen{qw(begin end)} = (0,0);
1247             }
1248             }
1249              
1250 0         0 return @subs;
1251             }
1252              
1253             sub _write_begin {
1254 0     0   0 my $self = shift;
1255              
1256 0         0 my $INDENT = substr($self->{INDENT}, 0, length($self->{INDENT})-1);
1257              
1258 0         0 $self->_subst_makecode('begin');
1259 0         0 $self->{Data}{begin} =~ s/(\$INDENT)/$1/eego;
  0         0  
1260 0         0 $self->_do_verbose("\n", File::Basename::basename($self->{Config}{Build_PL}), " written:\n", 2);
1261 0         0 $self->_do_verbose('-' x ($self->{Config}{Build_PL_Length} + 9), "\n", 2);
1262 0         0 $self->_do_verbose($self->{Data}{begin}, 2);
1263              
1264 0         0 print $self->{Data}{begin};
1265             }
1266              
1267             sub _write_args {
1268 0     0   0 my $self = shift;
1269              
1270 0         0 my $arg;
1271 0         0 my $regex = '$chunk =~ /=> \{/';
1272              
1273 0 0       0 if (@{$self->{make_code}{args}{begin}||[]}) {
  0 0       0  
1274 0         0 foreach my $codechunk (@{$self->{make_code}{args}{begin}}) {
  0         0  
1275 0 0       0 if (ref $codechunk eq 'ARRAY') {
1276 0         0 foreach my $code (@$codechunk) {
1277 0         0 $self->_do_verbose("$self->{INDENT}$code\n", 2);
1278 0         0 print "$self->{INDENT}$code\n";
1279             }
1280             } else {
1281 0         0 $self->_do_verbose("$self->{INDENT}$codechunk\n", 2);
1282 0         0 print "$self->{INDENT}$codechunk\n";
1283             }
1284             }
1285             }
1286              
1287 0         0 foreach my $chunk (@{$self->{buildargs_dumped}}) {
  0         0  
1288             # Hash/Array output
1289 0 0       0 if ($chunk =~ /=> [\{\[]/) {
1290              
1291             # Remove redundant parentheses
1292 0 0       0 $chunk =~ s/^\{.*?\n(.*(?{ $regex ? '\}' : '\]' }))\s+\}\s+$/$1/os;
  0         0  
1293              
1294             # One element per each line
1295 0         0 my @lines;
1296 0         0 push @lines, $1 while $chunk =~ s/^(.*?\n)(.*)$/$2/s;
1297              
1298             # Gather whitespace up to hash key in order
1299             # to recreate native Dump() indentation.
1300 0         0 my ($whitespace) = $lines[0] =~ /^(\s+)(\w+)/;
1301 0         0 $arg = $2;
1302 0         0 my $shorten = length($whitespace);
1303              
1304 0         0 foreach (my $i = 0; $i < @lines; $i++) {
1305 0         0 my $line = $lines[$i];
1306 0         0 chomp $line;
1307             # Remove additional whitespace
1308 0         0 $line =~ s/^\s{$shorten}(.*)$/$1/o;
1309              
1310             # Quote sub hash keys
1311 0 0       0 $line =~ s/^(\s+)([\w:]+)/$1'$2'/ if $line =~ /^\s+/;
1312              
1313             # Add comma where appropriate (version numbers, parentheses, brackets)
1314 0 0       0 $line .= ',' if $line =~ /[\d+ \} \]] $/x;
1315              
1316             # (De)quotify numbers, variables & code bits
1317 0         0 $line =~ s/' \\? ( \d | [\\ \/ \( \) \$ \@ \%]+ \w+) '/$1/gx;
1318 0 0       0 $self->_quotify(\$line) if $line =~ /\(/;
1319              
1320             # Add comma to dequotified key/value pairs
1321 0 0 0     0 my $comma = ',' if $line =~ /['"](?!,)$/ && $#lines - $i != 1;
1322 0   0     0 $comma ||= '';
1323              
1324             # Construct line output
1325 0         0 my $output = "$self->{INDENT}$line$comma";
1326              
1327             # Add adhering comments at end of array/hash
1328 0 0 0     0 $output .= ($i == $#lines && defined $self->{make_comments}{$arg})
1329             ? "$self->{make_comments}{$arg}\n"
1330             : "\n";
1331              
1332             # Output line
1333 0         0 $self->_do_verbose($output, 2);
1334 0         0 print $output;
1335             }
1336             # String output
1337             } else {
1338 0         0 chomp $chunk;
1339             # Remove redundant parentheses
1340 0         0 $chunk =~ s/^\{\s+(.*?)\s+\}$/$1/sx;
1341              
1342             # (De)quotify numbers, variables & code bits
1343 0         0 $chunk =~ s/' \\? ( \d | [\\ \/ \( \) \$ \@ \%]+ \w+ ) '/$1/gx;
1344 0 0       0 $self->_quotify(\$chunk) if $chunk =~ /\(/;
1345              
1346             # Extract argument (key)
1347 0         0 ($arg) = $chunk =~ /^\s*(\w+)/;
1348              
1349             # Construct line output & add adhering comment
1350 0         0 my $output = "$self->{INDENT}$chunk,";
1351 0 0       0 $output .= $self->{make_comments}{$arg} if defined $self->{make_comments}{$arg};
1352              
1353             # Output key/value pair
1354 0         0 $self->_do_verbose("$output\n", 2);
1355 0         0 print "$output\n";
1356             }
1357              
1358 3     3   23 no warnings 'uninitialized';
  3         5  
  3         2422  
1359 0         0 my @args;
1360              
1361 0 0       0 if ($self->{make_code}{args}{$arg}) {
1362 0         0 @args = ();
1363 0         0 foreach my $arg (@{$self->{make_code}{args}{$arg}}) {
  0         0  
1364 0 0       0 if (ref $arg eq 'ARRAY') {
1365 0         0 push @args, @$arg;
1366             } else {
1367 0         0 push @args, $arg;
1368             }
1369             }
1370              
1371 0         0 foreach $arg (@args) {
1372 0 0       0 next unless $arg;
1373              
1374 0 0       0 $arg .= ',' unless $arg =~ /^\#/;
1375              
1376 0         0 $self->_do_verbose("$self->{INDENT}$arg\n", 2);
1377 0         0 print "$self->{INDENT}$arg\n";
1378             }
1379             }
1380             }
1381             }
1382              
1383             sub _quotify {
1384 0     0   0 my ($self, $string) = @_;
1385              
1386             # Removing single-quotes and escaping backslashes
1387 0         0 $$string =~ s/(=>\s+?)'/$1/;
1388 0         0 $$string =~ s/',?$//;
1389 0         0 $$string =~ s/\\'/'/g;
1390              
1391             # Double-quoting $(NAME) variables
1392 0 0       0 if ($$string =~ /\$\(/) {
1393 0         0 $$string =~ s/(=>\s+?)(.*)/$1"$2"/;
1394             }
1395             }
1396              
1397             sub _write_end {
1398 0     0   0 my $self = shift;
1399              
1400 0         0 my $INDENT = substr($self->{INDENT}, 0, length($self->{INDENT})-1);
1401              
1402 0         0 $self->_subst_makecode('end');
1403 0         0 $self->{Data}{end} =~ s/(\$INDENT)/$1/eego;
  0         0  
1404 0         0 $self->_do_verbose($self->{Data}{end}, 2);
1405              
1406 0         0 print $self->{Data}{end};
1407             }
1408              
1409             sub _subst_makecode {
1410 0     0   0 my ($self, $section) = @_;
1411              
1412 0   0     0 $self->{make_code}{$section} ||= '';
1413              
1414             $self->{make_code}{$section} =~ /\w/
1415             ? $self->{Data}{$section} =~ s/\$MAKECODE/$self->{make_code}{$section}/o
1416 0 0       0 : $self->{Data}{$section} =~ s/\n\$MAKECODE\n//o;
1417             }
1418              
1419             sub _add_to_manifest {
1420 0     0   0 my $self = shift;
1421              
1422 0 0       0 my $fh = IO::File->new($self->{Config}{MANIFEST}, '<')
1423             or die "Can't open $self->{Config}{MANIFEST}: $!\n";
1424 0         0 my @manifest = <$fh>;
1425 0         0 $fh->close;
1426              
1427 0         0 my $build_pl = File::Basename::basename($self->{Config}{Build_PL});
1428              
1429 0 0       0 unless (grep { /^$build_pl\s+$/o } @manifest) {
  0         0  
1430 0         0 unshift @manifest, "$build_pl\n";
1431              
1432 0 0       0 $fh = IO::File->new($self->{Config}{MANIFEST}, '>')
1433             or die "Can't open $self->{Config}{MANIFEST}: $!\n";
1434 0         0 print {$fh} sort @manifest;
  0         0  
1435 0         0 $fh->close;
1436              
1437 0         0 $self->_do_verbose(LEADCHAR."Added to $self->{Config}{MANIFEST}: $self->{Config}{Build_PL}\n");
1438             }
1439             }
1440              
1441             sub _show_summary {
1442 0     0   0 my $self = shift;
1443              
1444 0         0 my @summary = (
1445             [ 'Succeeded', 'succeeded' ],
1446             [ 'Skipped', 'skipped' ],
1447             [ 'Failed', 'failed' ],
1448             [ 'Method: parse', 'method_parse' ],
1449             [ 'Method: execute', 'method_execute' ],
1450             );
1451              
1452 0         0 local $" = "\n";
1453              
1454 0         0 foreach my $item (@summary) {
1455 0 0       0 next unless @{$self->{summary}{$item->[1]}||[]};
  0 0       0  
1456              
1457 0         0 $self->_do_verbose("$item->[0]\n");
1458 0         0 $self->_do_verbose('-' x length($item->[0]), "\n");
1459 0         0 $self->_do_verbose("@{$self->{summary}{$item->[1]}}\n\n");
  0         0  
1460             }
1461              
1462 0         0 my $howmany = @{$self->{summary}->{succeeded}};
  0         0  
1463              
1464 0         0 print "Processed $howmany directories\n";
1465             }
1466              
1467             sub _do_verbose {
1468 1     1   2 my $self = shift;
1469              
1470 1 50       5 my $level = $_[-1] =~ /^\d$/ ? pop : 1;
1471              
1472 1 50 33     8 if (($self->{Config}{Verbose} && $level == 1)
      33        
      33        
1473             || ($self->{Config}{Verbose} == 2 && $level == 2)) {
1474 0           print STDOUT @_;
1475             }
1476             }
1477              
1478             sub _debug {
1479 0     0     my $self = shift;
1480              
1481 0 0         if ($self->{Config}{Debug}) {
1482 0 0 0       pop and my $no_wait = 1 if $_[-1] eq 'no_wait';
1483 0           warn @_;
1484 0 0 0       warn "Press [enter] to continue...\n"
1485             and unless $no_wait;
1486             }
1487             }
1488              
1489             1;
1490             __DATA__