File Coverage

blib/lib/CPAN/Reporter.pm
Criterion Covered Total %
statement 523 611 85.6
branch 226 306 73.8
condition 83 120 69.1
subroutine 59 61 96.7
pod 0 6 0.0
total 891 1104 80.7


line stmt bran cond sub pod time code
1 33     33   95881 use strict;
  33         45  
  33         1462  
2             package CPAN::Reporter;
3              
4             our $VERSION = '1.2018';
5              
6 33     33   113 use Config;
  33         37  
  33         1159  
7 33     33   14716 use Capture::Tiny qw/ capture tee_merged /;
  33         140649  
  33         1941  
8 33     33   148 use CPAN 1.94 ();
  33         536  
  33         509  
9             #CPAN.pm was split into separate files in this version
10             #set minimum to it for simplicity
11 33     33   12588 use CPAN::Version ();
  33         39337  
  33         642  
12 33     33   156 use File::Basename qw/basename dirname/;
  33         31  
  33         1512  
13 33     33   124 use File::Find ();
  33         37  
  33         319  
14 33     33   90 use File::HomeDir ();
  33         36  
  33         438  
15 33     33   95 use File::Path qw/mkpath rmtree/;
  33         35  
  33         1330  
16 33     33   105 use File::Spec 3.19 ();
  33         561  
  33         595  
17 33     33   96 use File::Temp 0.16 qw/tempdir/;
  33         533  
  33         1036  
18 33     33   1271 use IO::File ();
  33         2108  
  33         365  
19 33     33   12537 use Parse::CPAN::Meta ();
  33         24651  
  33         497  
20 33     33   829 use Probe::Perl ();
  33         2090  
  33         473  
21 33     33   1426 use Test::Reporter 1.54 ();
  33         25726  
  33         434  
22 33     33   13789 use CPAN::Reporter::Config ();
  33         70  
  33         721  
23 33     33   13918 use CPAN::Reporter::History ();
  33         62  
  33         676  
24 33     33   11763 use CPAN::Reporter::PrereqCheck ();
  33         57  
  33         614  
25              
26 33     33   138 use constant MAX_OUTPUT_LENGTH => 1_000_000;
  33         34  
  33         1872  
27              
28             #--------------------------------------------------------------------------#
29             # create temp lib dir for Devel::Autoflush
30             # so that PERL5OPT=-MDevel::Autoflush is found by any perl
31             #--------------------------------------------------------------------------#
32              
33 33     33   13259 use Devel::Autoflush 0.04 ();
  33         1079  
  33         176760  
34             # directory fixture
35             my $Autoflush_Lib = tempdir(
36             "CPAN-Reporter-lib-XXXX", TMPDIR => 1, CLEANUP => 1
37             );
38             # copy Devel::Autoflush to directory or clear autoflush_lib variable
39             _file_copy_quiet(
40             $INC{'Devel/Autoflush.pm'},
41             File::Spec->catfile( $Autoflush_Lib, qw/Devel Autoflush.pm/ )
42             ) or undef $Autoflush_Lib;
43              
44             #--------------------------------------------------------------------------#
45             # public API
46             #--------------------------------------------------------------------------#
47              
48             sub configure {
49 2     2 0 8696 goto &CPAN::Reporter::Config::_configure;
50             }
51              
52             sub grade_make {
53 16     16 0 1347 my @args = @_;
54 16 100       108 my $result = _init_result( 'make', @args ) or return;
55 15         62 _compute_make_grade($result);
56 15 100       60 if ( $result->{grade} eq 'discard' ) {
57             $CPAN::Frontend->myprint(
58             "\nCPAN::Reporter: test results were not valid, $result->{grade_msg}.\n\n",
59 4         180 $result->{prereq_pm}, "\n",
60             "Test report will not be sent"
61             );
62 4 100       102 CPAN::Reporter::History::_record_history( $result )
63             if not CPAN::Reporter::History::_is_duplicate( $result );
64             }
65             else {
66 11         65 _print_grade_msg($result->{make_cmd}, $result);
67 11 100       65 if ( $result->{grade} ne 'pass' ) { _dispatch_report( $result ) }
  9         38  
68             }
69 15         540 return $result->{success};
70             }
71              
72             sub grade_PL {
73 34     34 0 7163 my @args = @_;
74 34 100       231 my $result = _init_result( 'PL', @args ) or return;
75 33         121 _compute_PL_grade($result);
76 33 100       110 if ( $result->{grade} eq 'discard' ) {
77             $CPAN::Frontend->myprint(
78             "\nCPAN::Reporter: test results were not valid, $result->{grade_msg}.\n\n",
79 9         198 $result->{prereq_pm}, "\n",
80             "Test report will not be sent"
81             );
82 9 100       225 CPAN::Reporter::History::_record_history( $result )
83             if not CPAN::Reporter::History::_is_duplicate( $result );
84             }
85             else {
86 24         145 _print_grade_msg($result->{PL_file} , $result);
87 24 100       93 if ( $result->{grade} ne 'pass' ) { _dispatch_report( $result ) }
  18         83  
88             }
89 33         1248 return $result->{success};
90             }
91              
92             sub grade_test {
93 119     119 0 2046 my @args = @_;
94 119 100       665 my $result = _init_result( 'test', @args ) or return;
95 118         424 _compute_test_grade($result);
96 118 100       362 if ( $result->{grade} eq 'discard' ) {
97             $CPAN::Frontend->myprint(
98             "\nCPAN::Reporter: test results were not valid, $result->{grade_msg}.\n\n",
99 15         251 $result->{prereq_pm}, "\n",
100             "Test report will not be sent"
101             );
102 15 100       390 CPAN::Reporter::History::_record_history( $result )
103             if not CPAN::Reporter::History::_is_duplicate( $result );
104             }
105             else {
106 103         530 _print_grade_msg( "Test", $result );
107 103         407 _dispatch_report( $result );
108             }
109 118         4285 return $result->{success};
110             }
111              
112             sub record_command {
113 186     186 0 40968713 my ($command, $timeout) = @_;
114              
115             # XXX refactor this!
116             # Get configuration options
117 186 100       1513 if ( -r CPAN::Reporter::Config::_get_config_file() ) {
118 170         1067 my $config_obj = CPAN::Reporter::Config::_open_config_file();
119 170         264 my $config;
120 170 50       895 $config = CPAN::Reporter::Config::_get_config_options( $config_obj )
121             if $config_obj;
122              
123 170   100     1484 $timeout ||= $config->{command_timeout}; # might still be undef
124             }
125              
126 186         982 my ($cmd, $redirect) = _split_redirect($command);
127              
128             # Teeing a command loses its exit value so we must wrap the command
129             # and print the exit code so we can read it off of output
130 186         301 my $wrap_code;
131 186 100       471 if ( $timeout ) {
132 15 50       80 $wrap_code = $^O eq 'MSWin32'
133             ? _timeout_wrapper_win32($cmd, $timeout)
134             : _timeout_wrapper($cmd, $timeout);
135             }
136             # if no timeout or timeout wrap code wasn't available
137 186 100       468 if ( ! $wrap_code ) {
138 171         428 my $safecmd = quotemeta($cmd);
139 171         670 $wrap_code = << "HERE";
140             my \$rc = system("$safecmd");
141             my \$ec = \$rc == -1 ? -1 : \$?;
142             print "($safecmd exited with \$ec)\\n";
143             HERE
144             }
145              
146             # write code to a tempfile for execution
147 186         534 my $wrapper_name = _temp_filename( 'CPAN-Reporter-CW-' );
148 186 50       2193 my $wrapper_fh = IO::File->new( $wrapper_name, 'w' )
149             or die "Could not create a wrapper for $cmd\: $!";
150              
151 186         29912 $wrapper_fh->print( $wrap_code );
152 186         2407 $wrapper_fh->close;
153              
154             # tee the command wrapper
155 186         7796 my @tee_input = ( Probe::Perl->find_perl_interpreter, $wrapper_name );
156 186 100       2832 push @tee_input, $redirect if defined $redirect;
157 186         253 my $tee_out;
158             {
159             # ensure autoflush if we can
160 186 100       203 local $ENV{PERL5OPT} = _get_perl5opt() if _is_PL($command);
  186         744  
161 186     186   5750 $tee_out = tee_merged { system( @tee_input ) };
  186         119121009  
162             }
163              
164             # cleanup
165 186 50       3586855 unlink $wrapper_name unless $ENV{PERL_CR_NO_CLEANUP};
166              
167 186         20111 my @cmd_output = split qr{(?<=$/)}, $tee_out;
168 186 50       961 if ( ! @cmd_output ) {
169 0         0 $CPAN::Frontend->mywarn(
170             "CPAN::Reporter: didn't capture command results for '$cmd'\n"
171             );
172 0         0 return;
173             }
174              
175             # extract the exit value
176 186         378 my $exit_value;
177 186 50       1507 if ( $cmd_output[-1] =~ m{exited with} ) {
178 186         1519 ($exit_value) = $cmd_output[-1] =~ m{exited with ([-0-9]+)};
179 186         404 pop @cmd_output;
180             }
181              
182             # bail out on some errors
183 186 50       1274 if ( ! defined $exit_value ) {
    50          
184 0         0 $CPAN::Frontend->mywarn(
185             "CPAN::Reporter: couldn't determine exit value for '$cmd'\n"
186             );
187 0         0 return;
188             }
189             elsif ( $exit_value == -1 ) {
190 0         0 $CPAN::Frontend->mywarn(
191             "CPAN::Reporter: couldn't execute '$cmd'\n"
192             );
193 0         0 return;
194             }
195              
196 186         2767 return \@cmd_output, $exit_value;
197             }
198              
199             sub test {
200 76     76 0 13467931 my ($dist, $system_command) = @_;
201 76         355 my ($output, $exit_value) = record_command( $system_command );
202 76         335 return grade_test( $dist, $system_command, $output, $exit_value );
203             }
204              
205             #--------------------------------------------------------------------------#
206             # private functions
207             #--------------------------------------------------------------------------#
208              
209             #--------------------------------------------------------------------------#
210             # _compute_make_grade
211             #--------------------------------------------------------------------------#
212              
213             sub _compute_make_grade {
214 15     15   26 my $result = shift;
215 15         22 my ($grade,$msg);
216 15 100       43 if ( $result->{exit_value} ) {
217 13         55 $result->{grade} = "unknown";
218 13         44 $result->{grade_msg} = "Stopped with an error"
219             }
220             else {
221 2         5 $result->{grade} = "pass";
222 2         6 $result->{grade_msg} = "No errors"
223             }
224              
225 15         64 _downgrade_known_causes( $result );
226              
227 15         62 $result->{success} = $result->{grade} eq 'pass';
228 15         41 return;
229             }
230              
231             #--------------------------------------------------------------------------#
232             # _compute_PL_grade
233             #--------------------------------------------------------------------------#
234              
235             sub _compute_PL_grade {
236 33     33   62 my $result = shift;
237 33         38 my ($grade,$msg);
238 33 100       105 if ( $result->{exit_value} ) {
239 23         99 $result->{grade} = "unknown";
240 23         83 $result->{grade_msg} = "Stopped with an error"
241             }
242             else {
243 10         51 $result->{grade} = "pass";
244 10         36 $result->{grade_msg} = "No errors"
245             }
246              
247 33         177 _downgrade_known_causes( $result );
248              
249 33         129 $result->{success} = $result->{grade} eq 'pass';
250 33         84 return;
251             }
252              
253             #--------------------------------------------------------------------------#
254             # _compute_test_grade
255             #
256             # Don't shortcut to unknown unless _has_tests because a custom
257             # Makefile.PL or Build.PL might define tests in a non-standard way
258             #
259             # With test.pl and 'make test', any t/*.t might pass Test::Harness, but
260             # test.pl might still fail, or there might only be test.pl,
261             # so use exit code directly
262             #
263             # Likewise, if we have recursive Makefile.PL, then we don't trust the
264             # reverse-order parsing and should just take the exit code directly
265             #
266             # Otherwise, parse in reverse order for Test::Harness output or a couple
267             # other significant strings and stop after the first match. Going in
268             # reverse and stopping is done to (hopefully) avoid picking up spurious
269             # results from any test output. But then we have to check for
270             # unsupported OS strings in case those were printed but were not fatal.
271             #--------------------------------------------------------------------------#
272              
273             sub _compute_test_grade {
274 118     118   176 my $result = shift;
275 118         163 my ($grade,$msg);
276 118         205 my $output = $result->{output};
277              
278             # In some cases, get a result straight from the exit code
279 118 100 100     1468 if ( $result->{is_make} && ( -f "test.pl" || _has_recursive_make() ) ) {
      66        
280 16 100       99 if ( $result->{exit_value} ) {
281 10         33 $grade = "fail";
282 10         25 $msg = "'make test' error detected";
283             }
284             else {
285 6         14 $grade = "pass";
286 6         16 $msg = "'make test' no errors";
287             }
288             }
289             # Otherwise, get a result from Test::Harness output
290             else {
291             # figure out the right harness parser
292 102         473 _expand_result( $result );
293 102         375 my $harness_version = $result->{toolchain}{'Test::Harness'}{have};
294 102 50       1366 my $harness_parser = CPAN::Version->vgt($harness_version, '2.99_01')
295             ? \&_parse_tap_harness
296             : \&_parse_test_harness;
297             # parse lines in reverse
298 102         5655 for my $i ( reverse 0 .. $#{$output} ) {
  102         471  
299 276 100       1714 if ( $output->[$i] =~ m{No support for OS|OS unsupported}ims ) { # from any *.t file
    100          
300 6         20 $grade = 'na';
301 6         10 $msg = 'This platform is not supported';
302             }
303             elsif ( $output->[$i] =~ m{^.?No tests defined}ms ) { # from M::B
304 8         24 $grade = 'unknown';
305 8         15 $msg = 'No tests provided';
306             }
307             else {
308 262         483 ($grade, $msg) = $harness_parser->( $output->[$i] );
309             }
310 276 100       566 last if $grade;
311             }
312             # fallback on exit value if no recognizable Test::Harness output
313 102 100       361 if ( ! $grade ) {
314 12 100       77 $grade = $result->{exit_value} ? "fail" : "pass";
315             $msg = ( $result->{is_make} ? "'make test' " : "'Build test' " )
316 12 100       82 . ( $result->{exit_value} ? "error detected" : "no errors");
    100          
317             }
318             }
319              
320 118         478 $result->{grade} = $grade;
321 118         333 $result->{grade_msg} = $msg;
322              
323 118         516 _downgrade_known_causes( $result );
324              
325             $result->{success} = $result->{grade} eq 'pass'
326 118   100     743 || $result->{grade} eq 'unknown';
327 118         248 return;
328             }
329              
330             #--------------------------------------------------------------------------#
331             # _dispatch_report
332             #
333             # Set up Test::Reporter and prompt user for edit, send
334             #--------------------------------------------------------------------------#
335              
336             sub _dispatch_report {
337 131     131   5676 my $result = shift;
338 131         340 my $phase = $result->{phase};
339              
340 131         561 $CPAN::Frontend->myprint(
341             "CPAN::Reporter: preparing a CPAN Testers report for $result->{dist_name}\n"
342             );
343              
344             # Get configuration options
345 131         1737 my $config_obj = CPAN::Reporter::Config::_open_config_file();
346 131         213 my $config;
347 131 100       694 $config = CPAN::Reporter::Config::_get_config_options( $config_obj )
348             if $config_obj;
349 131 100       431 if ( ! $config->{email_from} ) {
350 5         11 $CPAN::Frontend->mywarn( << "EMAIL_REQUIRED");
351              
352             CPAN::Reporter: required 'email_from' option missing an email address, so
353             test report will not be sent. See documentation for configuration details.
354              
355             Even though CPAN Testers no longer uses email, this email address will
356             show up in the report and help identify the tester. This is required
357             for compatibility with tools that process legacy reports for analysis.
358              
359             EMAIL_REQUIRED
360 5         45 return;
361             }
362              
363             # Need to know if this is a duplicate
364 126         544 my $is_duplicate = CPAN::Reporter::History::_is_duplicate( $result );
365              
366             # Abort if the distribution name is not formatted according to
367             # CPAN Testers requirements: Dist-Name-version.suffix
368             # Regex from CPAN-Testers should extract name, separator, version
369             # and extension
370             my @format_checks = $result->{dist_basename} =~
371 126         1717 m{(.+)([\-\_])(v?\d.*)(\.(?:tar\.(?:gz|bz2)|tgz|zip))$}i;
372             ;
373 126 100       325 if ( ! grep { length } @format_checks ) {
  492         695  
374 3         27 $CPAN::Frontend->mywarn( << "END_BAD_DISTNAME");
375              
376             CPAN::Reporter: the distribution name '$result->{dist_basename}' does not
377             appear to be packaged according to CPAN tester guidelines. Perhaps it is
378             not a normal CPAN distribution.
379              
380             Test report will not be sent.
381              
382             END_BAD_DISTNAME
383              
384             # record this as a discard, instead
385 3         39 $result->{grade} = 'discard';
386 3 50       14 CPAN::Reporter::History::_record_history( $result )
387             if not $is_duplicate;
388 3         14 return;
389             }
390              
391             # Gather 'expensive' data for the report
392 123         293 _expand_result( $result);
393              
394             # Skip if distribution name matches the send_skipfile
395 123 100 66     459 if ( $config->{send_skipfile} && -r $config->{send_skipfile} ) {
396 4         31 my $send_skipfile = IO::File->new( $config->{send_skipfile}, "r" );
397 4         323 my $dist_id = $result->{dist}->pretty_id;
398 4         55 while ( my $pattern = <$send_skipfile> ) {
399 11         17 chomp($pattern);
400             # ignore comments
401 11 100       31 next if substr($pattern,0,1) eq '#';
402             # if it doesn't match, continue with next pattern
403 7 100       106 next if $dist_id !~ /$pattern/i;
404             # if it matches, warn and return
405 3         22 $CPAN::Frontend->myprint( << "END_SKIP_DIST" );
406             CPAN::Reporter: '$dist_id' matched against the send_skipfile.
407              
408             Test report will not be sent.
409              
410             END_SKIP_DIST
411              
412 3         67 return;
413             }
414             }
415              
416             # Setup the test report
417 120         1646 my $tr = Test::Reporter->new;
418 120         1826 $tr->grade( $result->{grade} );
419 120         1177 $tr->distribution( $result->{dist_name} );
420             # Older Test::Reporter doesn't support distfile, but we need it for
421             # Metabase transport
422             $tr->distfile( $result->{dist}->pretty_id )
423 120 50       1616 if $Test::Reporter::VERSION >= 1.54;
424              
425             # Skip if duplicate and not sending duplicates
426 120 100       1605 if ( $is_duplicate ) {
427 74 100       263 if ( _prompt( $config, "send_duplicates", $tr->grade) =~ /^n/ ) {
428 2         6 $CPAN::Frontend->myprint(<< "DUPLICATE_REPORT");
429              
430             CPAN::Reporter: this appears to be a duplicate report for the $phase phase:
431 2         10 @{[$tr->subject]}
432              
433             Test report will not be sent.
434              
435             DUPLICATE_REPORT
436              
437 2         78 return;
438             }
439             }
440              
441             # Set debug and transport options, if supported
442 118 50       319 $tr->debug( $config->{debug} ) if defined $config->{debug};
443 118         208 my $transport = $config->{transport};
444 118 50 33     720 unless ( defined $transport && length $transport ) {
445 0         0 $CPAN::Frontend->mywarn( << "TRANSPORT_REQUIRED");
446              
447             CPAN::Reporter: required 'transport' option missing so the test report
448             will not be sent. See documentation for configuration details.
449              
450             TRANSPORT_REQUIRED
451 0         0 return;
452             }
453 118         452 my @transport_args = split " ", $transport;
454              
455             # special hack for Metabase arguments
456 118 100       337 if ($transport_args[0] eq 'Metabase') {
457 116         337 @transport_args = _validate_metabase_args(@transport_args);
458 116 50       366 unless (@transport_args) {
459 0         0 $CPAN::Frontend->mywarn( "Test report will not be sent.\n\n" );
460 0         0 return;
461             }
462             }
463              
464 118         175 eval { $tr->transport( @transport_args ) };
  118         422  
465 118 100       1866 if ($@) {
466 1         10 $CPAN::Frontend->mywarn(
467             "CPAN::Reporter: problem with Test::Reporter transport: \n" .
468             "$@\n" .
469             "Test report will not be sent\n"
470             );
471 1         18 return;
472             }
473              
474             # prepare mail transport
475 117         575 $tr->from( $config->{email_from} );
476              
477             # Populate the test report
478 117         897 $tr->comments( _report_text( $result ) );
479 117         988 $tr->via( 'CPAN::Reporter ' . $CPAN::Reporter::VERSION );
480              
481             # prompt for editing report
482 117 50       803 if ( _prompt( $config, "edit_report", $tr->grade ) =~ /^y/ ) {
483 0         0 my $editor = $config->{editor};
484 0 0       0 local $ENV{VISUAL} = $editor if $editor; ## no critic
485 0         0 $tr->edit_comments;
486             }
487              
488             # send_*_report can override send_report
489 117 100       564 my $send_config = defined $config->{"send_$phase\_report"}
490             ? "send_$phase\_report"
491             : "send_report" ;
492 117 100       548 if ( _prompt( $config, $send_config, $tr->grade ) =~ /^y/ ) {
493 114         469 $CPAN::Frontend->myprint( "CPAN::Reporter: sending test report with '" . $tr->grade .
494             "' via " . $transport_args[0] . "\n");
495 114 50       2503 if ( $tr->send() ) {
496 114 100       825 CPAN::Reporter::History::_record_history( $result )
497             if not $is_duplicate;
498             }
499             else {
500 0         0 $CPAN::Frontend->mywarn( "CPAN::Reporter: " . $tr->errstr . "\n");
501              
502 0 0       0 if ( $config->{retry_submission} ) {
503 0         0 sleep(3);
504              
505 0         0 $CPAN::Frontend->mywarn( "CPAN::Reporter: second attempt\n");
506 0         0 $tr->errstr('');
507              
508 0 0       0 if ( $tr->send() ) {
509 0 0       0 CPAN::Reporter::History::_record_history( $result )
510             if not $is_duplicate;
511             }
512             else {
513 0         0 $CPAN::Frontend->mywarn( "CPAN::Reporter: " . $tr->errstr . "\n");
514             }
515             }
516              
517             }
518             }
519             else {
520 3         11 $CPAN::Frontend->myprint("CPAN::Reporter: test report will not be sent\n");
521             }
522              
523 117         830 return;
524             }
525              
526             sub _report_timeout {
527 129     129   239 my $result = shift;
528 129 100       461 if ($result->{exit_value} == 9) {
529 2         18 my $config_obj = CPAN::Reporter::Config::_open_config_file();
530 2         5 my $config;
531 2 50       13 $config = CPAN::Reporter::Config::_get_config_options( $config_obj )
532             if $config_obj;
533              
534 2 50       6 if ($config->{'_store_problems_in_dir'}) {
535 0         0 my $distribution = $result->{dist}->base_id;
536 0         0 my $file = "e9.$distribution.${\(time)}.$$.log";
  0         0  
537 0 0       0 if (open my $to_log_fh, '>>', $config->{'_store_problems_in_dir'}.'/'.$file) {
538 0         0 print $to_log_fh $distribution,"\n";
539 0         0 print $to_log_fh "stage: ",$result->{phase},"\n";
540 0         0 print $to_log_fh $Config{archname},"\n";
541 0         0 print $to_log_fh _report_text( $result );
542             } else {
543             $CPAN::Frontend->mywarn( "CPAN::Reporter: writing ".
544 0         0 $config->{'_store_problems_in_dir'}.'/'.$file. " failed\n");
545             }
546             }
547 2 50       12 if ($config->{'_problem_log'}) {
548 0         0 my $distribution = $result->{dist}->base_id;
549 0 0       0 if (open my $to_log_fh, '>>', $config->{'_problem_log'}) {
550 0         0 print $to_log_fh "$result->{phase} $distribution $Config{archname}\n";
551             } else {
552             $CPAN::Frontend->mywarn( "CPAN::Reporter: writing ".
553 0         0 $config->{'_store_problems_in_dir'}. " failed\n");
554             }
555             }
556             }
557             }
558              
559             #--------------------------------------------------------------------------#
560             # _downgrade_known_causes
561             # Downgrade failure/unknown grade if we can determine a cause
562             # If platform not supported => 'na'
563             # If perl version is too low => 'na'
564             # If stated prereqs missing => 'discard'
565             #--------------------------------------------------------------------------#
566              
567             sub _downgrade_known_causes {
568 169     169   645 my ($result) = @_;
569 169         771 my ($grade, $output) = ( $result->{grade}, $result->{output} );
570 169   50     524 my $msg = $result->{grade_msg} || q{};
571              
572             # shortcut unless fail/unknown; but PL might look like pass but actually
573             # have "OS Unsupported" messages if someone printed message and then
574             # did "exit 0"
575 169 100       453 return if $grade eq 'na';
576 163 100 100     812 return if $grade eq 'pass' && $result->{phase} ne 'PL';
577              
578             # get prereqs
579 129         368 _expand_result( $result );
580              
581 129         608 _report_timeout( $result );
582              
583             # if process was halted with a signal, just set for discard and return
584 129 100       440 if ( $result->{exit_value} & 127 ) {
585 2         7 $result->{grade} = 'discard';
586 2         3 $result->{grade_msg} = 'Command interrupted';
587 2         5 return;
588             }
589              
590             # look for perl version error messages from various programs
591             # "Error evaling..." type errors happen on Perl < 5.006 when modules
592             # define their version with "our $VERSION = ..."
593 127         188 my ($harness_error, $version_error, $unsupported) ;
594 127         401 for my $line ( @$output ) {
595 3614 100 100     9130 if ( $result->{phase} eq 'test'
596             && $line =~ m{open3: IO::Pipe: Can't spawn.*?TAP/Parser/Iterator/Process.pm}
597             ) {
598 2         9 $harness_error++;
599 2         6 last;
600             }
601 3612 50 66     30479 if( $line =~ /(?
      100        
      66        
      66        
      33        
602             #?
603             $line =~ /Perl version .*? or higher required\. We run/ims || #EU::MM
604             $line =~ /ERROR: perl: Version .*? is installed, but we need version/ims ||
605             $line =~ /ERROR: perl \(.*?\) is installed, but we need version/ims ||
606             $line =~ /Error evaling version line 'BEGIN/ims ||
607             $line =~ /Could not eval '/ims
608             ) {
609 8         27 $version_error++;
610 8         21 last;
611             }
612 3604 100       10117 if ( $line =~ /No support for OS|OS unsupported/ims ) {
613 6         24 $unsupported++;
614 6         18 last;
615             }
616             }
617              
618             # if the test harness had an error, discard the report
619 127 100 100     3400 if ( $harness_error ) {
    100 100        
    100 100        
    100 100        
    100 100        
    100 100        
    100 100        
    100 100        
    100 100        
      100        
620 2         9 $grade = 'discard';
621 2         7 $msg = 'Test harness failure';
622             }
623             # check for explicit version error or just a perl version prerequisite
624             elsif ( $version_error || $result->{prereq_pm} =~ m{^\s+!\s+perl\s}ims ) {
625 12         34 $grade = 'na';
626 12         32 $msg = 'Perl version too low';
627             }
628             # check again for unsupported OS in case we took 'fail' from exit value
629             elsif ( $unsupported ) {
630 6         26 $grade = 'na';
631 6         18 $msg = 'This platform is not supported';
632             }
633             # check for Makefile without 'test' target; there are lots
634             # of variations on the error message, e.g. "target test", "target 'test'",
635             # "'test'", "`test'" and so on.
636             elsif (
637             $result->{is_make} && $result->{phase} eq 'test' && ! _has_test_target()
638             ) {
639 1         3 $grade = 'unknown';
640 1         4 $msg = 'No make test target';
641             }
642             # check the prereq report for missing or failure flag '!'
643             elsif ( $grade ne 'pass' && $result->{prereq_pm} =~ m{n/a}ims ) {
644 14         139 $grade = 'discard';
645 14         54 $msg = "Prerequisite missing:\n$result->{prereq_pm}";
646             }
647             elsif ( $grade ne 'pass' && $result->{prereq_pm} =~ m{^\s+!}ims ) {
648 8         28 $grade = 'discard';
649 8         35 $msg = "Prerequisite version too low:\n$result->{prereq_pm}";
650             }
651             # in PL stage -- if pass but no Makefile or Build, then this should
652             # be recorded as a discard
653             elsif ( $result->{phase} eq 'PL' && $grade eq 'pass'
654             && ! -f 'Makefile' && ! -f 'Build'
655             ) {
656 2         10 $grade = 'discard';
657 2         6 $msg = 'No Makefile or Build file found';
658             }
659             elsif ( $result->{command} =~ /Build.*?-j/ ) {
660 2         6 $grade = 'discard';
661 2         6 $msg = '-j is not a valid option for Module::Build (upgrade your CPAN.pm)';
662             }
663             elsif (
664             $result->{is_make} && $result->{phase} eq 'make' &&
665 39         87 grep { /Makefile out-of-date with respect to Makefile.PL/ } @$output
666             ) {
667 1         3 $grade = 'discard';
668 1         3 $msg = 'Makefile out-of-date';
669             }
670              
671             # store results
672 127         1937 $result->{grade} = $grade;
673 127         221 $result->{grade_msg} = $msg;
674              
675 127         309 return;
676             }
677              
678             #--------------------------------------------------------------------------#
679             # _expand_result - add expensive information like prerequisites and
680             # toolchain that should only be generated if a report will actually
681             # be sent
682             #--------------------------------------------------------------------------#
683              
684             sub _expand_result {
685 354     354   609 my $result = shift;
686 354 100       1356 return if $result->{expanded}++; # only do this once
687 167         822 $result->{prereq_pm} = _prereq_report( $result->{dist} );
688             {
689             # mirror PERL5OPT as in record_command
690 167 100       281 local $ENV{PERL5OPT} = _get_perl5opt() if _is_PL($result->{command});
  167         801  
691 167         676 $result->{env_vars} = _env_report();
692             }
693 167         649 $result->{special_vars} = _special_vars_report();
694 167         645 $result->{toolchain_versions} = _toolchain_report( $result );
695 167         1274 $result->{perl_version} = CPAN::Reporter::History::_format_perl_version();
696 167         605 return;
697             }
698              
699             #--------------------------------------------------------------------------#
700             # _env_report
701             #--------------------------------------------------------------------------#
702              
703             # Entries bracketed with "/" are taken to be a regex; otherwise literal
704             my @env_vars= qw(
705             /HARNESS/
706             /LC_/
707             /PERL/
708             /_TEST/
709             CCFLAGS
710             COMSPEC
711             INCLUDE
712             INSTALL_BASE
713             LANG
714             LANGUAGE
715             LD_LIBRARY_PATH
716             LDFLAGS
717             LIB
718             NON_INTERACTIVE
719             NUMBER_OF_PROCESSORS
720             PATH
721             PREFIX
722             PROCESSOR_IDENTIFIER
723             SHELL
724             TERM
725             TEMP
726             TMPDIR
727             );
728              
729             sub _env_report {
730 176     176   1115284 my @vars_found;
731 176         665 for my $var ( @env_vars ) {
732 3872 100       6983 if ( $var =~ m{^/(.+)/$} ) {
733 704         3211 push @vars_found, grep { /$1/ } keys %ENV;
  16324         28484  
734             }
735             else {
736 3168 100       5607 push @vars_found, $var if exists $ENV{$var};
737             }
738             }
739              
740 176         344 my $report = "";
741 176         1353 for my $var ( sort @vars_found ) {
742 2145         2230 my $value = $ENV{$var};
743 2145 50       2579 $value = '[undef]' if ! defined $value;
744 2145         3138 $report .= " $var = $value\n";
745             }
746 176         947 return $report;
747             }
748              
749             #--------------------------------------------------------------------------#
750             # _file_copy_quiet
751             #
752             # manual file copy -- quietly return undef on failure
753             #--------------------------------------------------------------------------#
754              
755             sub _file_copy_quiet {
756 33     33   65 my ($source, $target) = @_;
757             # ensure we have a target directory
758 33 50       5204 mkpath( dirname($target) ) or return;
759             # read source
760 33         101 local *FH;
761 33 50       1179 open FH, "<$source" or return; ## no critic
762 33         54 my $pm_guts = do { local $/; };
  33         119  
  33         542  
763 33         183 close FH;
764             # write target
765 33 50       1562 open FH, ">$target" or return; ## no critic
766 33         132 print FH $pm_guts;
767 33         952 close FH;
768 33         164 return 1;
769             }
770              
771             #--------------------------------------------------------------------------#
772             # _get_perl5opt
773             #--------------------------------------------------------------------------#
774              
775             sub _get_perl5opt {
776 76   50 76   582 my $perl5opt = $ENV{PERL5OPT} || q{};
777 76 50       198 if ( $Autoflush_Lib ) {
778 76 50       206 $perl5opt .= q{ } if length $perl5opt;
779 76 50       396 $perl5opt .= "-I$Autoflush_Lib " if $] >= 5.008;
780 76         143 $perl5opt .= "-MDevel::Autoflush";
781             }
782 76         720 return $perl5opt;
783             }
784              
785             #--------------------------------------------------------------------------#
786             # _has_recursive_make
787             #
788             # Ignore Makefile.PL in t directories
789             #--------------------------------------------------------------------------#
790              
791             sub _has_recursive_make {
792 67     67   101 my $PL_count = 0;
793             File::Find::find(
794             sub {
795 2318 100   2318   64424 if ( $_ eq 't' ) {
    100          
796 63         431 $File::Find::prune = 1;
797             }
798             elsif ( $_ eq 'Makefile.PL' ) {
799 73         949 $PL_count++;
800             }
801             },
802 67         6253 File::Spec->curdir()
803             );
804 67         557 return $PL_count > 1;
805             }
806              
807             #--------------------------------------------------------------------------#
808             # _has_test_target
809             #--------------------------------------------------------------------------#
810              
811             sub _has_test_target {
812 47 50   47   626 my $fh = IO::File->new("Makefile") or return;
813 47         17469 return scalar grep { /^test[ ]*:/ } <$fh>;
  40793         32303  
814             }
815              
816             #--------------------------------------------------------------------------#
817             # _init_result -- create and return a hash of values for use in
818             # report evaluation and dispatch
819             #
820             # takes same argument format as grade_*()
821             #--------------------------------------------------------------------------#
822              
823             sub _init_result {
824 173     173   7555 my ($phase, $dist, $system_command, $output, $exit_value) = @_;
825              
826 173 50 33     1434 unless ( defined $output && defined $exit_value ) {
827 0         0 my $missing;
828 0 0 0     0 if ( ! defined $output && ! defined $exit_value ) {
    0 0        
829 0         0 $missing = "exit value and output"
830             }
831             elsif ( defined $output && !defined $exit_value ) {
832 0         0 $missing = "exit value"
833             }
834             else {
835 0         0 $missing = "output";
836             }
837 0         0 $CPAN::Frontend->mywarn(
838             "CPAN::Reporter: had errors capturing $missing. Tests abandoned"
839             );
840 0         0 return;
841             }
842              
843 173 100       1492 if ( $dist->pretty_id =~ m{\w+/Perl6/} ) {
844 3         41 $CPAN::Frontend->mywarn(
845             "CPAN::Reporter: Won't report a Perl6 distribution."
846             );
847 3         135 return;
848             }
849              
850 170 100       2009 my $result = {
851             phase => $phase,
852             dist => $dist,
853             command => $system_command,
854             is_make => _is_make( $system_command ),
855             output => ref $output eq 'ARRAY' ? $output : [ split /\n/, $output ],
856             exit_value => $exit_value,
857             # Note: pretty_id is like "DAGOLDEN/CPAN-Reporter-0.40.tar.gz"
858             dist_basename => basename($dist->pretty_id),
859             dist_name => $dist->base_id,
860             };
861              
862             # Used in messages to user
863 170 100       19170 $result->{PL_file} = $result->{is_make} ? "Makefile.PL" : "Build.PL";
864 170 100       2492 $result->{make_cmd} = $result->{is_make} ? $Config{make} : "Build";
865              
866             # CPAN might fail to find an author object for some strange dists
867 170         854 my $author = $dist->author;
868 170 50       1192 $result->{author} = defined $author ? $author->fullname : "Author";
869 170 50       1412 $result->{author_id} = defined $author ? $author->id : "" ;
870              
871 170         1239 return $result;
872             }
873              
874             #--------------------------------------------------------------------------#
875             # _is_make
876             #--------------------------------------------------------------------------#
877              
878             sub _is_make {
879 190     190   8783 my $command = shift;
880 190 100       2537 return $command =~ m{\b(?:\S*make|Makefile.PL)\b}ims ? 1 : 0;
881             }
882              
883             #--------------------------------------------------------------------------#
884             # _is_PL
885             #--------------------------------------------------------------------------#
886              
887             sub _is_PL {
888 353     353   678 my $command = shift;
889 353 100       3602 return $command =~ m{\b(?:Makefile|Build)\.PL\b}ims ? 1 : 0;
890             }
891              
892             #--------------------------------------------------------------------------#
893             # _max_length
894             #--------------------------------------------------------------------------#
895              
896             sub _max_length {
897 352     352   1228 my ($first, @rest) = @_;
898 352         457 my $max = length $first;
899 352         680 for my $term ( @rest ) {
900 6688 100       8086 $max = length $term if length $term > $max;
901             }
902 352         832 return $max;
903             }
904              
905              
906             #--------------------------------------------------------------------------#
907             # _parse_tap_harness
908             #
909             # As of Test::Harness 2.99_02, the final line is provided by TAP::Harness
910             # as "Result: STATUS" where STATUS is "PASS", "FAIL" or "NOTESTS"
911             #--------------------------------------------------------------------------#
912              
913              
914             sub _parse_tap_harness {
915 262     262   330 my ($line) = @_;
916 262 100       1191 if ( $line =~ m{^Result:\s+([A-Z]+)} ) {
    100          
917 74 100       357 if ( $1 eq 'PASS' ) {
    100          
    50          
918 20         175 return ('pass', 'All tests successful');
919             }
920             elsif ( $1 eq 'FAIL' ) {
921 51         168 return ('fail', 'One or more tests failed');
922             }
923             elsif ( $1 eq 'NOTESTS' ) {
924 3         16 return ('unknown', 'No tests were run');
925             }
926             }
927             elsif ( $line =~ m{Bailout called\.\s+Further testing stopped}ms ) {
928 2         6 return ( 'fail', 'Bailed out of tests');
929             }
930 186         277 return;
931             }
932              
933             #--------------------------------------------------------------------------#
934             # _parse_test_harness
935             #
936             # Output strings taken from Test::Harness::
937             # _show_results() -- for versions < 2.57_03
938             # get_results() -- for versions >= 2.57_03
939             #--------------------------------------------------------------------------#
940              
941             sub _parse_test_harness {
942 0     0   0 my ($line) = @_;
943 0 0       0 if ( $line =~ m{^All tests successful}ms ) {
    0          
    0          
    0          
    0          
944 0         0 return ( 'pass', 'All tests successful' );
945             }
946             elsif ( $line =~ m{^FAILED--no tests were run}ms ) {
947 0         0 return ( 'unknown', 'No tests were run' );
948             }
949             elsif ( $line =~ m{^FAILED--.*--no output}ms ) {
950 0         0 return ( 'unknown', 'No tests were run');
951             }
952             elsif ( $line =~ m{FAILED--Further testing stopped}ms ) {
953 0         0 return ( 'fail', 'Bailed out of tests');
954             }
955             elsif ( $line =~ m{^Failed }ms ) { # must be lowercase
956 0         0 return ( 'fail', 'One or more tests failed');
957             }
958             else {
959 0         0 return;
960             }
961             }
962              
963             #--------------------------------------------------------------------------#
964             # _prereq_report
965             #--------------------------------------------------------------------------#
966              
967             my @prereq_sections = qw(
968             requires build_requires configure_requires opt_requires opt_build_requires
969             );
970              
971             sub _prereq_report {
972 179     179   3848485 my $dist = shift;
973 179         260 my (%need, %have, %prereq_met, $report);
974              
975             # Extract requires/build_requires from CPAN dist
976 179         598 my $prereq_pm = $dist->prereq_pm;
977              
978 179 50       1206 if ( ref $prereq_pm eq 'HASH' ) {
979             # CPAN 1.94 returns hash with requires/build_requires # so no need to support old style
980 179         782 foreach (values %$prereq_pm) {
981 711 50 66     2905 if (defined && ref ne 'HASH') {
982 0         0 require Data::Dumper;
983 0         0 warn "Data error detecting prerequisites. Please report it to CPAN::Reporter bug tracker:";
984 0         0 warn Data::Dumper::Dumper($prereq_pm);
985 0         0 die "Stopping";
986             }
987             }
988              
989 179         672 for my $sec ( @prereq_sections ) {
990 895 100       667 $need{$sec} = $prereq_pm->{$sec} if keys %{ $prereq_pm->{$sec} };
  895         3067  
991             }
992             }
993              
994             # Extract configure_requires from META.yml if it exists
995 179 100 66     1702 if ( $dist->{build_dir} && -d $dist->{build_dir} ) {
996 72         1247 my $meta_yml = File::Spec->catfile($dist->{build_dir}, 'META.yml');
997 72 100       975 if ( -f $meta_yml ) {
998 4         18 my @yaml = eval { Parse::CPAN::Meta::LoadFile($meta_yml) };
  4         35  
999 4 100       11047 if ( $@ ) {
1000 2         39 $CPAN::Frontend->mywarn(
1001             "CPAN::Reporter: error parsing META.yml\n"
1002             );
1003             }
1004 4 100 66     87 if ( ref $yaml[0] eq 'HASH' &&
1005             ref $yaml[0]{configure_requires} eq 'HASH'
1006             ) {
1007 2         20 $need{configure_requires} = $yaml[0]{configure_requires};
1008             }
1009             }
1010             }
1011              
1012             # see what prereqs are satisfied in subprocess
1013 179         422 for my $section ( @prereq_sections ) {
1014 895 100       2365 next unless ref $need{$section} eq 'HASH';
1015 137         194 my @prereq_list = %{ $need{$section} };
  137         644  
1016 137 50       396 next unless @prereq_list;
1017 137         627 my $prereq_results = _version_finder( @prereq_list );
1018 137         247 for my $mod ( keys %{$prereq_results} ) {
  137         612  
1019 185         775 $have{$section}{$mod} = $prereq_results->{$mod}{have};
1020 185         1040 $prereq_met{$section}{$mod} = $prereq_results->{$mod}{met};
1021             }
1022             }
1023              
1024             # find formatting widths
1025 179         427 my ($name_width, $need_width, $have_width) = (6, 4, 4);
1026 179         358 for my $section ( @prereq_sections ) {
1027 895         732 for my $module ( keys %{ $need{$section} } ) {
  895         2582  
1028 185         262 my $name_length = length $module;
1029 185         476 my $need_length = length $need{$section}{$module};
1030 185         289 my $have_length = length $have{$section}{$module};
1031 185 100       468 $name_width = $name_length if $name_length > $name_width;
1032 185 100       389 $need_width = $need_length if $need_length > $need_width;
1033 185 100       501 $have_width = $have_length if $have_length > $have_width;
1034             }
1035             }
1036              
1037 179         800 my $format_str =
1038             " \%1s \%-${name_width}s \%-${need_width}s \%-${have_width}s\n";
1039              
1040             # generate the report
1041 179         401 for my $section ( @prereq_sections ) {
1042 895 100       599 if ( keys %{ $need{$section} } ) {
  895         2182  
1043 137         433 $report .= "$section:\n\n";
1044 137         779 $report .= sprintf( $format_str, " ", qw/Module Need Have/ );
1045 137         698 $report .= sprintf( $format_str, " ",
1046             "-" x $name_width,
1047             "-" x $need_width,
1048             "-" x $have_width );
1049 137         274 for my $module (sort {lc $a cmp lc $b} keys %{ $need{$section} } ) {
  142         112  
  137         644  
1050 185         363 my $need = $need{$section}{$module};
1051 185         277 my $have = $have{$section}{$module};
1052 185 100       524 my $bad = $prereq_met{$section}{$module} ? " " : "!";
1053 185         603 $report .=
1054             sprintf( $format_str, $bad, $module, $need, $have);
1055             }
1056 137         287 $report .= "\n";
1057             }
1058             }
1059              
1060 179   100     2147 return $report || " No requirements found\n";
1061             }
1062              
1063             #--------------------------------------------------------------------------#
1064             # _print_grade_msg -
1065             #--------------------------------------------------------------------------#
1066              
1067             sub _print_grade_msg {
1068 138     138   448 my ($phase, $result) = @_;
1069 138         334 my ($grade, $msg) = ($result->{grade}, $result->{grade_msg});
1070 138         2471 $CPAN::Frontend->myprint( "CPAN::Reporter: $phase result is '$grade'");
1071 138 50 33     3983 $CPAN::Frontend->myprint(", $msg") if defined $msg && length $msg;
1072 138         1218 $CPAN::Frontend->myprint(".\n");
1073 138         894 return;
1074             }
1075              
1076             #--------------------------------------------------------------------------#
1077             # _prompt
1078             #
1079             # Note: always returns lowercase
1080             #--------------------------------------------------------------------------#
1081              
1082             sub _prompt {
1083 328     328   16195 my ($config, $option, $grade, $extra) = @_;
1084 328   50     1263 $extra ||= q{};
1085              
1086 328         746 my %spec = CPAN::Reporter::Config::_config_spec();
1087              
1088             my $dispatch = CPAN::Reporter::Config::_validate_grade_action_pair(
1089 328   50     1812 $option, join(q{ }, "default:no", $config->{$option} || '')
1090             );
1091 328   66     1150 my $action = $dispatch->{$grade} || $dispatch->{default};
1092 328         715 my $intro = $spec{$option}{prompt} . $extra . " (yes/no)";
1093 328         270 my $prompt;
1094 328 100       1075 if ( $action =~ m{^ask/yes}i ) {
    100          
1095 10         22 $prompt = CPAN::Shell::colorable_makemaker_prompt( $intro, "yes" );
1096             }
1097             elsif ( $action =~ m{^ask(/no)?}i ) {
1098 72         305 $prompt = CPAN::Shell::colorable_makemaker_prompt( $intro, "no" );
1099             }
1100             else {
1101 246         305 $prompt = $action;
1102             }
1103 328         6179 return lc $prompt;
1104             }
1105              
1106             #--------------------------------------------------------------------------#
1107             # _report_text
1108             #--------------------------------------------------------------------------#
1109              
1110             my %intro_para = (
1111             'pass' => <<'HERE',
1112             Thank you for uploading your work to CPAN. Congratulations!
1113             All tests were successful.
1114             HERE
1115              
1116             'fail' => <<'HERE',
1117             Thank you for uploading your work to CPAN. However, there was a problem
1118             testing your distribution.
1119              
1120             If you think this report is invalid, please consult the CPAN Testers Wiki
1121             for suggestions on how to avoid getting FAIL reports for missing library
1122             or binary dependencies, unsupported operating systems, and so on:
1123              
1124             http://wiki.cpantesters.org/wiki/CPANAuthorNotes
1125             HERE
1126              
1127             'unknown' => <<'HERE',
1128             Thank you for uploading your work to CPAN. However, attempting to
1129             test your distribution gave an inconclusive result.
1130              
1131             This could be because your distribution had an error during the make/build
1132             stage, did not define tests, tests could not be found, because your tests were
1133             interrupted before they finished, or because the results of the tests could not
1134             be parsed. You may wish to consult the CPAN Testers Wiki:
1135              
1136             http://wiki.cpantesters.org/wiki/CPANAuthorNotes
1137             HERE
1138              
1139             'na' => <<'HERE',
1140             Thank you for uploading your work to CPAN. While attempting to build or test
1141             this distribution, the distribution signaled that support is not available
1142             either for this operating system or this version of Perl. Nevertheless, any
1143             diagnostic output produced is provided below for reference. If this is not
1144             what you expect, you may wish to consult the CPAN Testers Wiki:
1145              
1146             http://wiki.cpantesters.org/wiki/CPANAuthorNotes
1147             HERE
1148              
1149             );
1150              
1151             sub _comment_text {
1152              
1153             # We assemble the completed comment as a series of "parts" which
1154             # will get joined together
1155 117     117   120 my @comment_parts;
1156              
1157             # All automated testing gets a preamble
1158 117 100       342 if ($ENV{AUTOMATED_TESTING}) {
1159 111         257 push @comment_parts,
1160             "this report is from an automated smoke testing program\n"
1161             . "and was not reviewed by a human for accuracy"
1162             }
1163              
1164             # If a comment file is provided, read it and add it to the comment
1165 117         270 my $confdir = CPAN::Reporter::Config::_get_config_dir();
1166 117         680 my $comment_file = File::Spec->catfile($confdir, 'comment.txt');
1167 117 100 66     122443 if ( -d $confdir && -f $comment_file && -r $comment_file ) {
      66        
1168 2 50   1   97 open my $fh, '<:encoding(UTF-8)', $comment_file or die($!);
  1         7  
  1         1  
  1         13  
1169 2         1425 my $text;
1170 2         4 do {
1171 2         10 local $/ = undef; # No record (line) seperator on input
1172 2 50       51 defined( $text = <$fh> ) or die($!);
1173             };
1174 2         41 chomp($text);
1175 2         5 push @comment_parts, $text;
1176 2         27 close $fh;
1177             }
1178              
1179             # If we have an empty comment so far, add a default value
1180 117 100       332 if (scalar(@comment_parts) == 0) {
1181 5         14 push @comment_parts, 'none provided';
1182             }
1183              
1184             # Join the parts seperated by a blank line
1185 117         432 return join "\n\n", @comment_parts;
1186             }
1187              
1188             sub _report_text {
1189 117     117   180 my $data = shift;
1190 117         139 my $test_log = join(q{},@{$data->{output}});
  117         714  
1191 117 50       639 if ( length $test_log > MAX_OUTPUT_LENGTH ) {
1192 0         0 $test_log = substr( $test_log, 0, MAX_OUTPUT_LENGTH) . "\n";
1193 0         0 my $max_k = int(MAX_OUTPUT_LENGTH/1000) . "K";
1194 0         0 $test_log .= "\n[Output truncated after $max_k]\n\n";
1195             }
1196              
1197 117         267 my $comment_body = _comment_text();
1198              
1199             # generate report
1200 117         2015 my $output = << "ENDREPORT";
1201             Dear $data->{author},
1202              
1203             This is a computer-generated report for $data->{dist_name}
1204             on perl $data->{perl_version}, created by CPAN-Reporter-$CPAN::Reporter::VERSION\.
1205              
1206             $intro_para{ $data->{grade} }
1207             Sections of this report:
1208              
1209             * Tester comments
1210             * Program output
1211             * Prerequisites
1212             * Environment and other context
1213              
1214             ------------------------------
1215             TESTER COMMENTS
1216             ------------------------------
1217              
1218             Additional comments from tester:
1219              
1220             $comment_body
1221              
1222             ------------------------------
1223             PROGRAM OUTPUT
1224             ------------------------------
1225              
1226             Output from '$data->{command}':
1227              
1228             $test_log
1229             ------------------------------
1230             PREREQUISITES
1231             ------------------------------
1232              
1233             Prerequisite modules loaded:
1234              
1235             $data->{prereq_pm}
1236             ------------------------------
1237             ENVIRONMENT AND OTHER CONTEXT
1238             ------------------------------
1239              
1240             Environment variables:
1241              
1242             $data->{env_vars}
1243             Perl special variables (and OS-specific diagnostics, for MSWin32):
1244              
1245             $data->{special_vars}
1246             Perl module toolchain versions installed:
1247              
1248             $data->{toolchain_versions}
1249             ENDREPORT
1250              
1251 117         459 return $output;
1252             }
1253              
1254             #--------------------------------------------------------------------------#
1255             # _special_vars_report
1256             #--------------------------------------------------------------------------#
1257              
1258             sub _special_vars_report {
1259 176     176   6595 my $special_vars = << "HERE";
1260             \$^X = $^X
1261             \$UID/\$EUID = $< / $>
1262             \$GID = $(
1263             \$EGID = $)
1264             HERE
1265 176 50 33     716 if ( $^O eq 'MSWin32' && eval "require Win32" ) { ## no critic
1266 0         0 my @getosversion = Win32::GetOSVersion();
1267 0         0 my $getosversion = join(", ", @getosversion);
1268 0         0 $special_vars .= " Win32::GetOSName = " . Win32::GetOSName() . "\n";
1269 0         0 $special_vars .= " Win32::GetOSVersion = $getosversion\n";
1270 0         0 $special_vars .= " Win32::FsType = " . Win32::FsType() . "\n";
1271 0         0 $special_vars .= " Win32::IsAdminUser = " . Win32::IsAdminUser() . "\n";
1272             }
1273 176         652 return $special_vars;
1274             }
1275              
1276             #--------------------------------------------------------------------------#
1277             # _split_redirect
1278             #--------------------------------------------------------------------------#
1279              
1280             sub _split_redirect {
1281 186     186   288 my $command = shift;
1282 186         551 my ($cmd, $prefix) = ($command =~ m{\A(.+?)(\|.*)\z});
1283 186 100       439 if (defined $cmd) {
1284 1         5 return ($cmd, $prefix);
1285             }
1286             else { # didn't match a redirection
1287 185         407 return $command
1288             }
1289             }
1290              
1291             #--------------------------------------------------------------------------#
1292             # _temp_filename -- stand-in for File::Temp for backwards compatibility
1293             #
1294             # takes an optional prefix, adds 8 random chars and returns
1295             # an absolute pathname
1296             #
1297             # NOTE -- manual unlink required
1298             #--------------------------------------------------------------------------#
1299              
1300             # @CHARS from File::Temp
1301             my @CHARS = (qw/ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
1302             a b c d e f g h i j k l m n o p q r s t u v w x y z
1303             0 1 2 3 4 5 6 7 8 9 _
1304             /);
1305              
1306             sub _temp_filename {
1307 500     500   1150 my ($prefix) = @_;
1308 500 50       1315 $prefix = q{} unless defined $prefix;
1309 500         5863 $prefix .= $CHARS[ int( rand(@CHARS) ) ] for 0 .. 7;
1310 500         12055 return File::Spec->catfile(File::Spec->tmpdir(), $prefix);
1311             }
1312              
1313             #--------------------------------------------------------------------------#
1314             # _timeout_wrapper
1315             # Timeout technique adapted from App::cpanminus (thank you Miyagawa!)
1316             #--------------------------------------------------------------------------#
1317              
1318             sub _timeout_wrapper {
1319 15     15   22 my ($cmd, $timeout) = @_;
1320              
1321             # protect shell quotes
1322 15         35 $cmd = quotemeta($cmd);
1323              
1324 15         90 my $wrapper = sprintf << 'HERE', $timeout, $cmd, $cmd;
1325             use strict;
1326             my ($pid, $exitcode);
1327             eval {
1328             $pid = fork;
1329             if ($pid) {
1330             local $SIG{CHLD};
1331             local $SIG{ALRM} = sub {die 'Timeout'};
1332             alarm %s;
1333             my $wstat = waitpid $pid, 0;
1334             alarm 0;
1335             $exitcode = $wstat == -1 ? -1 : $?;
1336             } elsif ( $pid == 0 ) {
1337             setpgrp(0,0); # new process group
1338             exec "%s";
1339             }
1340             else {
1341             die "Cannot fork: $!\n" unless defined $pid;
1342             }
1343             };
1344             if ($pid && $@ =~ /Timeout/){
1345             kill -9 => $pid; # and send to our child's whole process group
1346             waitpid $pid, 0;
1347             $exitcode = 9; # force result to look like SIGKILL
1348             }
1349             elsif ($@) {
1350             die $@;
1351             }
1352             print "(%s exited with $exitcode)\n";
1353             HERE
1354 15         33 return $wrapper;
1355             }
1356              
1357             #--------------------------------------------------------------------------#
1358             # _timeout_wrapper_win32
1359             #--------------------------------------------------------------------------#
1360              
1361             sub _timeout_wrapper_win32 {
1362 0     0   0 my ($cmd, $timeout) = @_;
1363              
1364 0   0     0 $timeout ||= 0; # just in case upstream doesn't guarantee it
1365              
1366 0         0 eval "use Win32::Job ();";
1367 0 0       0 if ($@) {
1368 0         0 $CPAN::Frontend->mywarn( << 'HERE' );
1369             CPAN::Reporter: you need Win32::Job for inactivity_timeout support.
1370             Continuing without timeout...
1371             HERE
1372 0         0 return;
1373             }
1374              
1375 0         0 my ($program) = split " ", $cmd;
1376 0 0       0 if (! File::Spec->file_name_is_absolute( $program ) ) {
1377 0         0 my $exe = $program . ".exe";
1378 0         0 my ($path) = grep { -e File::Spec->catfile($_,$exe) }
1379 0         0 split /$Config{path_sep}/, $ENV{PATH};
1380 0 0       0 if (! $path) {
1381 0         0 $CPAN::Frontend->mywarn( << "HERE" );
1382             CPAN::Reporter: can't locate $exe in the PATH.
1383             Continuing without timeout...
1384             HERE
1385 0         0 return;
1386             }
1387 0         0 $program = File::Spec->catfile($path,$exe);
1388             }
1389              
1390             # protect shell quotes and other things
1391 0         0 $_ = quotemeta($_) for ($program, $cmd);
1392              
1393 0         0 my $wrapper = sprintf << 'HERE', $program, $cmd, $timeout;
1394             use strict;
1395             use Win32::Job;
1396             my $executable = "%s";
1397             my $cmd_line = "%s";
1398             my $timeout = %s;
1399              
1400             my $job = Win32::Job->new() or die $^E;
1401             my $ppid = $job->spawn($executable, $cmd_line);
1402             $job->run($timeout);
1403             my $status = $job->status;
1404             my $exitcode = $status->{$ppid}{exitcode};
1405             if ( $exitcode == 293 ) {
1406             $exitcode = 9; # map Win32::Job kill (293) to SIGKILL (9)
1407             }
1408             elsif ( $exitcode & 255 ) {
1409             $exitcode = $exitcode << 8; # how perl expects it
1410             }
1411             print "($cmd_line exited with $exitcode)\n";
1412             HERE
1413 0         0 return $wrapper;
1414             }
1415              
1416             #--------------------------------------------------------------------------#-
1417             # _toolchain_report
1418             #--------------------------------------------------------------------------#
1419              
1420             my @toolchain_mods= qw(
1421             CPAN
1422             CPAN::Meta
1423             Cwd
1424             ExtUtils::CBuilder
1425             ExtUtils::Command
1426             ExtUtils::Install
1427             ExtUtils::MakeMaker
1428             ExtUtils::Manifest
1429             ExtUtils::ParseXS
1430             File::Spec
1431             JSON
1432             JSON::PP
1433             Module::Build
1434             Module::Signature
1435             Parse::CPAN::Meta
1436             Test::Harness
1437             Test::More
1438             YAML
1439             YAML::Syck
1440             version
1441             );
1442              
1443             sub _toolchain_report {
1444 176     176   2501 my ($result) = @_;
1445              
1446 176         497 my $installed = _version_finder( map { $_ => 0 } @toolchain_mods );
  3520         4184  
1447 176         2060 $result->{toolchain} = $installed;
1448              
1449 176         1638 my $mod_width = _max_length( keys %$installed );
1450             my $ver_width = _max_length(
1451 176         751 map { $installed->{$_}{have} } keys %$installed
  3520         3472  
1452             );
1453              
1454 176         1057 my $format = " \%-${mod_width}s \%-${ver_width}s\n";
1455              
1456 176         353 my $report = "";
1457 176         1056 $report .= sprintf( $format, "Module", "Have" );
1458 176         801 $report .= sprintf( $format, "-" x $mod_width, "-" x $ver_width );
1459              
1460 176         2196 for my $var ( sort keys %$installed ) {
1461             $report .= sprintf(" \%-${mod_width}s \%-${ver_width}s\n",
1462 3520         5570 $var, $installed->{$var}{have} );
1463             }
1464              
1465 176         1142 return $report;
1466             }
1467              
1468              
1469             #--------------------------------------------------------------------------#
1470             # _validate_metabase_args
1471             #
1472             # This is a kludge to make metabase transport args a little less
1473             # clunky for novice users
1474             #--------------------------------------------------------------------------#
1475              
1476             sub _validate_metabase_args {
1477 116     116   304 my @transport_args = @_;
1478 116         225 shift @transport_args; # drop leading 'Metabase'
1479 116         127 my (%args, $error);
1480              
1481 116 50       424 if ( @transport_args % 2 != 0 ) {
1482 0         0 $error = << "TRANSPORT_ARGS";
1483              
1484             CPAN::Reporter: Metabase 'transport' option had odd number of
1485             parameters in the config file. See documentation for proper
1486             configuration format.
1487              
1488             TRANSPORT_ARGS
1489             }
1490             else {
1491 116         524 %args = @transport_args;
1492              
1493 116         270 for my $key ( qw/uri id_file/ ) {
1494 232 50       543 if ( ! $args{$key} ) {
1495 0         0 $error = << "TRANSPORT_ARGS";
1496              
1497             CPAN::Reporter: Metabase 'transport' option did not have
1498             a '$key' parameter in the config file. See documentation for
1499             proper configuration format.
1500              
1501             TRANSPORT_ARGS
1502             }
1503             }
1504             }
1505              
1506 116 50       273 if ( $error ) {
1507 0         0 $CPAN::Frontend->mywarn( $error );
1508 0         0 return;
1509             }
1510              
1511 116         352 $args{id_file} = CPAN::Reporter::Config::_normalize_id_file( $args{id_file} );
1512              
1513 116 50       1809 if ( ! -r $args{id_file} ) {
1514 0         0 $CPAN::Frontend->mywarn( <<"TRANSPORT_ARGS" );
1515              
1516             CPAN::Reporter: Could not find Metabase transport 'id_file' parameter
1517             located at '$args{id_file}'.
1518             See documentation for proper configuration of the 'transport' setting.
1519              
1520             TRANSPORT_ARGS
1521 0         0 return;
1522             }
1523              
1524 116         654 return ('Metabase', %args);
1525             }
1526              
1527              
1528             #--------------------------------------------------------------------------#
1529             # _version_finder
1530             #
1531             # module => version pairs
1532             #
1533             # This is done via an external program to show installed versions exactly
1534             # the way they would be found when test programs are run. This means that
1535             # any updates to PERL5LIB will be reflected in the results.
1536             #
1537             # File-finding logic taken from CPAN::Module::inst_file(). Logic to
1538             # handle newer Module::Build prereq syntax is taken from
1539             # CPAN::Distribution::unsat_prereq()
1540             #
1541             #--------------------------------------------------------------------------#
1542              
1543             my $version_finder = $INC{'CPAN/Reporter/PrereqCheck.pm'};
1544              
1545             sub _version_finder {
1546 314     314   5059 my %prereqs = @_;
1547              
1548 314         4861 my $perl = Probe::Perl->find_perl_interpreter();
1549 314         6682 my @prereq_results;
1550              
1551 314         1145 my $prereq_input = _temp_filename( 'CPAN-Reporter-PI-' );
1552 314 50       4226 my $fh = IO::File->new( $prereq_input, "w" )
1553             or die "Could not create temporary '$prereq_input' for prereq analysis: $!";
1554 314         59418 $fh->print( map { "$_ $prereqs{$_}\n" } keys %prereqs );
  3719         7696  
1555 314         4401 $fh->close;
1556              
1557 314     314   26488 my $prereq_result = capture { system( $perl, $version_finder, '<', $prereq_input ) };
  314         82091647  
1558              
1559 314         3242084 unlink $prereq_input;
1560              
1561 314         710 my %result;
1562 314         2556 for my $line ( split "\n", $prereq_result ) {
1563 3719 50       5835 next unless length $line;
1564 3719         7718 my ($mod, $met, $have) = split " ", $line;
1565 3719 50 33     17569 unless ( defined($mod) && defined($met) && defined($have) ) {
      33        
1566 0         0 $CPAN::Frontend->mywarn(
1567             "Error parsing output from CPAN::Reporter::PrereqCheck:\n" .
1568             $line
1569             );
1570 0         0 next;
1571             }
1572 3719         11040 $result{$mod}{have} = $have;
1573 3719         5433 $result{$mod}{met} = $met;
1574             }
1575 314         4207 return \%result;
1576             }
1577              
1578             1;
1579              
1580             # ABSTRACT: Adds CPAN Testers reporting to CPAN.pm
1581              
1582             =pod
1583              
1584             =encoding UTF-8
1585              
1586             =head1 NAME
1587              
1588             CPAN::Reporter - Adds CPAN Testers reporting to CPAN.pm
1589              
1590             =head1 VERSION
1591              
1592             version 1.2018
1593              
1594             =head1 SYNOPSIS
1595              
1596             From the CPAN shell:
1597              
1598             cpan> install Task::CPAN::Reporter
1599             cpan> reload cpan
1600             cpan> o conf init test_report
1601              
1602             Installing L will pull in additional dependencies
1603             that new CPAN Testers will need.
1604              
1605             Advanced CPAN Testers with custom L setups
1606             may wish to install only CPAN::Reporter, which has fewer dependencies.
1607              
1608             =head1 DESCRIPTION
1609              
1610             The CPAN Testers project captures and analyzes detailed results from building
1611             and testing CPAN distributions on multiple operating systems and multiple
1612             versions of Perl. This provides valuable feedback to module authors and
1613             potential users to identify bugs or platform compatibility issues and improves
1614             the overall quality and value of CPAN.
1615              
1616             One way individuals can contribute is to send a report for each module that
1617             they test or install. CPAN::Reporter is an add-on for the CPAN.pm module to
1618             send the results of building and testing modules to the CPAN Testers project.
1619             Full support for CPAN::Reporter is available in CPAN.pm as of version 1.92.
1620              
1621             =for Pod::Coverage configure
1622             grade_PL
1623             grade_make
1624             grade_test
1625             record_command
1626             test
1627              
1628             =head1 GETTING STARTED
1629              
1630             =head2 Installation
1631              
1632             The first step in using CPAN::Reporter is to install it using whatever
1633             version of CPAN.pm is already installed. CPAN.pm will be upgraded as
1634             a dependency if necessary.
1635              
1636             cpan> install CPAN::Reporter
1637              
1638             If CPAN.pm was upgraded, it needs to be reloaded.
1639              
1640             cpan> reload cpan
1641              
1642             =head2 Configuration
1643              
1644             If upgrading from a very old version of CPAN.pm, users may be prompted to renew
1645             their configuration settings, including the 'test_report' option to enable
1646             CPAN::Reporter.
1647              
1648             If not prompted automatically, users should manually initialize CPAN::Reporter
1649             support. After enabling CPAN::Reporter, CPAN.pm will automatically continue
1650             with interactive configuration of CPAN::Reporter options.
1651              
1652             cpan> o conf init test_report
1653              
1654             Users will need to enter an email address in one of the following formats:
1655              
1656             johndoe@example.com
1657             John Doe
1658             "John Q. Public"
1659              
1660             Users that are new to CPAN::Reporter should accept the recommended values
1661             for other configuration options.
1662              
1663             Users will be prompted to create a I file that uniquely
1664             identifies their test reports. See L below for details.
1665              
1666             After completing interactive configuration, be sure to commit (save) the CPAN
1667             configuration changes.
1668              
1669             cpan> o conf commit
1670              
1671             See L for advanced configuration settings.
1672              
1673             =head3 The Metabase
1674              
1675             CPAN::Reporter sends test reports to a server known as the Metabase. This
1676             requires an active Internet connection and a profile file. To create the
1677             profile, users will need to run C<<< metabase-profile >>> from a terminal window and
1678             fill the information at the prompts. This will create a file called
1679             C<<< metabase_id.json >>> in the current directory. That file should be moved to the
1680             C<<< .cpanreporter >>> directory inside the user's home directory.
1681              
1682             Users with an existing metabase profile file (e.g. from another machine),
1683             should copy it into the C<<< .cpanreporter >>> directory instead of creating
1684             a new one. Profile files may be located outside the C<<< .cpanreporter >>>
1685             directory by following instructions in L.
1686              
1687             =head3 Default Test Comments
1688              
1689             This module puts default text into the "TESTER COMMENTS" section, typically,
1690             "none provided" if doing interactive testing, or, if doing smoke testing that
1691             sets CE$ENV{AUTOMATED_TESTING}E to a true value, "this report is from an
1692             automated smoke testing program and was not reviewed by a human for
1693             accuracy." If CECPAN::ReporterE is configured to allow editing of the
1694             report, this can be edited during submission.
1695              
1696             If you wish to override the default comment, you can create a file named
1697             CEcomment.txtE in the configuration directory (typically C<<< .cpanreporter >>>
1698             under the user's home directory), with the default comment you would
1699             like to appear.
1700              
1701             Note that if your test is an automated smoke
1702             test (CE$ENV{AUTOMATED_TESTING}E is set to a true value), the smoke
1703             test notice ("this report is from an automated smoke testing program and
1704             was not reviewed by a human for accuracy") is included along with a blank
1705             line before your CEcomment.txtE, so that it is always possible to
1706             distinguish automated tests from non-automated tests that use this
1707             module.
1708              
1709             =head2 Using CPAN::Reporter
1710              
1711             Once CPAN::Reporter is enabled and configured, test or install modules with
1712             CPAN.pm as usual.
1713              
1714             For example, to test the File::Marker module:
1715              
1716             cpan> test File::Marker
1717              
1718             If a distribution's tests fail, users will be prompted to edit the report to
1719             add additional information that might help the author understand the failure.
1720              
1721             =head1 UNDERSTANDING TEST GRADES
1722              
1723             CPAN::Reporter will assign one of the following grades to the report:
1724              
1725             =over
1726              
1727             =item *
1728              
1729             C<<< pass >>> -- distribution built and tested correctly
1730              
1731             =item *
1732              
1733             C<<< fail >>> -- distribution failed to test correctly
1734              
1735             =item *
1736              
1737             C<<< unknown >>> -- distribution failed to build, had no test suite or outcome was
1738             inconclusive
1739              
1740             =item *
1741              
1742             C<<< na >>> --- distribution is not applicable to this platform andEor
1743             version of Perl
1744              
1745             =back
1746              
1747             In returning results of the test suite to CPAN.pm, "pass" and "unknown" are
1748             considered successful attempts to "make test" or "Build test" and will not
1749             prevent installation. "fail" and "na" are considered to be failures and
1750             CPAN.pm will not install unless forced.
1751              
1752             An error from Makefile.PLEBuild.PL or makeEBuild will also be graded as
1753             "unknown" and a failure will be signaled to CPAN.pm.
1754              
1755             If prerequisites specified in C<<< Makefile.PL >>> or C<<< Build.PL >>> are not available,
1756             no report will be generated and a failure will be signaled to CPAN.pm.
1757              
1758             =head1 PRIVACY WARNING
1759              
1760             CPAN::Reporter includes information in the test report about environment
1761             variables and special Perl variables that could be affecting test results in
1762             order to help module authors interpret the results of the tests. This includes
1763             information about paths, terminal, locale, userEgroup ID, installed toolchain
1764             modules (e.g. ExtUtils::MakeMaker) and so on.
1765              
1766             These have been intentionally limited to items that should not cause harmful
1767             personal information to be revealed -- it does I include your entire
1768             environment. Nevertheless, please do not use CPAN::Reporter if you are
1769             concerned about the disclosure of this information as part of your test report.
1770              
1771             Users wishing to review this information may choose to edit the report
1772             prior to sending it.
1773              
1774             =head1 BUGS
1775              
1776             Using command_timeout on Linux may cause problems. See
1777             L
1778              
1779             Please report any bugs or feature using the CPAN Request Tracker.
1780             Bugs can be submitted through the web interface at
1781             L
1782              
1783             When submitting a bug or request, please include a test-file or a patch to an
1784             existing test-file that illustrates the bug or desired feature.
1785              
1786             =head1 SEE ALSO
1787              
1788             Information about CPAN::Testers:
1789              
1790             =over
1791              
1792             =item *
1793              
1794             L -- overview of CPAN Testers architecture stack
1795              
1796             =item *
1797              
1798             L -- project home with all reports
1799              
1800             =item *
1801              
1802             L -- documentation and wiki
1803              
1804             =back
1805              
1806             Additional Documentation:
1807              
1808             =over
1809              
1810             =item *
1811              
1812             L -- advanced configuration settings
1813              
1814             =item *
1815              
1816             L -- hints and tips
1817              
1818             =back
1819              
1820             =for :stopwords cpan testmatrix url annocpan anno bugtracker rt cpants kwalitee diff irc mailto metadata placeholders metacpan
1821              
1822             =head1 SUPPORT
1823              
1824             =head2 Bugs / Feature Requests
1825              
1826             Please report any bugs or feature requests through the issue tracker
1827             at L.
1828             You will be notified automatically of any progress on your issue.
1829              
1830             =head2 Source Code
1831              
1832             This is open source software. The code repository is available for
1833             public review and contribution under the terms of the license.
1834              
1835             L
1836              
1837             git clone https://github.com/cpan-testers/CPAN-Reporter.git
1838              
1839             =head1 AUTHOR
1840              
1841             David Golden
1842              
1843             =head1 CONTRIBUTORS
1844              
1845             =for stopwords Alexandr Ciornii Breno G. de Oliveira Christian Walde Ed J Joel Maslak Kent Fredric Matthew Musgrove Patrice Clement Reini Urban Scott Wiersdorf Slaven Rezic
1846              
1847             =over 4
1848              
1849             =item *
1850              
1851             Alexandr Ciornii
1852              
1853             =item *
1854              
1855             Breno G. de Oliveira
1856              
1857             =item *
1858              
1859             Christian Walde
1860              
1861             =item *
1862              
1863             Ed J
1864              
1865             =item *
1866              
1867             Joel Maslak
1868              
1869             =item *
1870              
1871             Kent Fredric
1872              
1873             =item *
1874              
1875             Matthew Musgrove
1876              
1877             =item *
1878              
1879             Patrice Clement
1880              
1881             =item *
1882              
1883             Reini Urban
1884              
1885             =item *
1886              
1887             Scott Wiersdorf
1888              
1889             =item *
1890              
1891             Slaven Rezic
1892              
1893             =back
1894              
1895             =head1 COPYRIGHT AND LICENSE
1896              
1897             This software is Copyright (c) 2006 by David Golden.
1898              
1899             This is free software, licensed under:
1900              
1901             The Apache License, Version 2.0, January 2004
1902              
1903             =cut
1904              
1905             __END__