File Coverage

blib/lib/Module/Build/Convert.pm
Criterion Covered Total %
statement 77 883 8.7
branch 0 354 0.0
condition 0 227 0.0
subroutine 26 85 30.5
pod 2 2 100.0
total 105 1551 6.7


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