File Coverage

blib/lib/pler.pm
Criterion Covered Total %
statement 64 185 34.5
branch 4 74 5.4
condition 2 40 5.0
subroutine 20 56 35.7
pod 0 37 0.0
total 90 392 22.9


line stmt bran cond sub pod time code
1             package pler;
2              
3             # See 'sub main' for main functionality
4              
5 2     2   23368 use 5.00503;
  2         8  
  2         78  
6 2     2   11 use strict;
  2         4  
  2         68  
7 2     2   21 use Config;
  2         3  
  2         87  
8 2     2   69 use Carp ();
  2         4  
  2         57  
9 2     2   12 use Cwd 3.00 ();
  2         35  
  2         41  
10 2     2   1630 use File::Which 0.05 ();
  2         2316  
  2         51  
11 2     2   12 use File::Spec 0.80 ();
  2         39  
  2         43  
12 2     2   951 use File::Spec::Functions ':ALL';
  2         780  
  2         459  
13 2     2   1929 use File::Find::Rule 0.20 ();
  2         19578  
  2         133  
14 2     2   4618 use Getopt::Long 0 ();
  2         30631  
  2         114  
15 2     2   741 use Probe::Perl 0.01 ();
  2         1872  
  2         46  
16              
17 2     2   12 use vars qw{$VERSION};
  2         3  
  2         97  
18             BEGIN {
19 2     2   57 $VERSION = '1.06';
20             }
21              
22             # Does exec work on this platform
23 2   33 2   10 use constant EXEC_OK => ($^O ne 'MSWin32' and $^O ne 'cygwin');
  2         5  
  2         191  
24              
25             # Can you overwrite an open file on this platform
26 2     2   11 use constant OVERWRITE_OK => !! ( $^O ne 'MSWin32' );
  2         4  
  2         1524  
27              
28              
29              
30              
31              
32              
33             #####################################################################
34             # Resource Locations
35              
36             sub MakefilePL () {
37 0     0 0 0 catfile( curdir(), 'Makefile.PL' );
38             }
39              
40             sub BuildPL () {
41 0     0 0 0 catfile( curdir(), 'Build.PL' );
42             }
43              
44             sub Makefile () {
45 0     0 0 0 catfile( curdir(), 'Makefile' );
46             }
47              
48             sub Build () {
49 0     0 0 0 catfile( curdir(), 'Build' );
50             }
51              
52             sub perl () {
53 4     4 0 988 Probe::Perl->find_perl_interpreter;
54             }
55              
56             # Look for make in $Config
57             sub make () {
58 4     4 0 1190 my $make = $Config::Config{make};
59 4         3171 my $found = File::Which::which( $make );
60 4 50       706 unless ( $found ) {
61 0         0 Carp::croak("Failed to find '$make' (as specified by \$Config{make})");
62             }
63 4         34 return $found;
64             }
65              
66             sub blib () {
67 0     0 0 0 catdir( curdir(), 'blib' );
68             }
69              
70             sub inc () {
71 0     0 0 0 catdir( curdir(), 'inc' );
72             }
73              
74             sub lib () {
75 0     0 0 0 catdir( curdir(), 'lib' );
76             }
77              
78             sub t () {
79 0     0 0 0 catdir( curdir(), 't' );
80             }
81              
82             sub xt () {
83 0     0 0 0 catdir( curdir(), 'xt' );
84             }
85              
86              
87              
88              
89              
90             #####################################################################
91             # Convenience Logic
92              
93             sub has_makefilepl () {
94 0     0 0 0 !! -f MakefilePL;
95             }
96              
97             sub has_buildpl () {
98 0     0 0 0 !! -f BuildPL;
99             }
100              
101             sub has_makefile () {
102 0     0 0 0 !! -f Makefile;
103             }
104              
105             sub has_build () {
106 0     0 0 0 !! -f Build;
107             }
108              
109             sub has_blib () {
110 0     0 0 0 !! -d blib;
111             }
112              
113             sub blibpm () {
114 0     0 0 0 eval {
115 0         0 require blib;
116             };
117 0         0 return ! $@;
118             }
119              
120             sub has_inc () {
121 0     0 0 0 !! -f inc;
122             }
123              
124             sub has_lib () {
125 0     0 0 0 !! -d lib;
126             }
127              
128             sub has_t () {
129 0     0 0 0 !! -d t;
130             }
131              
132             sub has_xt () {
133 0     0 0 0 !! -d xt;
134             }
135              
136             sub in_distroot () {
137             !! (
138 0   0 0 0 0 has_makefilepl or (has_lib and has_t)
139             );
140             }
141              
142             sub in_subdir () {
143             !! (
144 0   0 0 0 0 -f catfile( updir(), 'Makefile.PL' )
145             or
146             -d catdir( updir(), 't' )
147             );
148             }
149              
150             sub needs_makefile () {
151 0 0   0 0 0 has_makefilepl and ! has_makefile;
152             }
153              
154             sub needs_build () {
155 0 0   0 0 0 has_buildpl and ! has_build;
156             }
157              
158             sub mtime ($) {
159 0     0 0 0 (stat($_[0]))[9];
160             }
161              
162             sub old_makefile () {
163 0 0 0 0 0 0 has_makefile
164             and
165             has_makefilepl
166             and
167             mtime(Makefile) < mtime(MakefilePL);
168             }
169              
170             sub old_build () {
171 0 0 0 0 0 0 has_build
172             and
173             has_buildpl
174             and
175             mtime(Build) < mtime(BuildPL);
176             }
177              
178              
179              
180              
181              
182             #####################################################################
183             # Utility Functions
184              
185             # Support verbosity
186 2     2   10 use vars qw{$VERBOSE};
  2         4  
  2         70  
187             BEGIN {
188 2   50 2   3168 $VERBOSE ||= 0;
189             }
190              
191             sub is_verbose {
192 0     0 0 0 $VERBOSE;
193             }
194              
195             sub verbose ($) {
196 0 0   0 0 0 message( $_[0] ) if $VERBOSE;
197             }
198              
199             sub message ($) {
200 0     0 0 0 print $_[0];
201             }
202              
203             sub error (@) {
204 0     0 0 0 print ' ' . join '', map { "$_\n" } ('', @_, '');
  0         0  
205 0         0 exit(255);
206             }
207              
208             sub run ($) {
209 0     0 0 0 my $cmd = shift;
210 0         0 verbose( "> $cmd" );
211 0         0 system( $cmd );
212             }
213              
214             sub handoff (@) {
215 0     0 0 0 my $cmd = join ' ', @_;
216 0         0 verbose( "> $cmd" );
217 0         0 $ENV{HARNESS_ACTIVE} = 1;
218 0         0 $ENV{RELEASE_TESTING} = 1;
219 0         0 if ( EXEC_OK ) {
220 0 0       0 exec( @_ ) or Carp::croak("Failed to exec '$cmd'");
221             } else {
222             system( @_ );
223             exit(0);
224             }
225             }
226              
227              
228              
229              
230              
231             #####################################################################
232             # Main Script
233              
234             my @SWITCHES = ();
235              
236             sub main {
237 0     0 0 0 Getopt::Long::Configure('no_ignore_case');
238             Getopt::Long::GetOptions(
239             'help' => \&help,
240 0     0   0 'V' => sub { print "pler $VERSION\n"; exit(0) },
  0         0  
241 0     0   0 'w' => sub { push @SWITCHES, '-w' },
242 0         0 );
243              
244             # Get the script name
245 0         0 my $script = shift @ARGV;
246 0 0       0 unless ( defined $script ) {
247 0         0 print "# No file name pattern provided, using 't'...\n";
248 0         0 $script = 't';
249             }
250              
251             # Abuse the highly mature logic in Cwd to define an $ENV{PWD} value
252             # by chdir'ing to the current directory.
253             # This lets us get the current directory without losing symlinks.
254 0         0 Cwd::chdir(curdir());
255 0 0       0 my $orig = $ENV{PWD} or die "Failed to get original directory";
256              
257             # Can we locate the distribution root
258 0         0 my ($v,$d,$f) = splitpath($ENV{PWD}, 'nofile');
259 0         0 my @dirs = splitdir($d);
260 0         0 while ( @dirs ) {
261 0         0 my $buildpl = catpath(
262             $v, catdir(@dirs), BuildPL,
263             );
264 0         0 my $makefilepl = catpath(
265             $v, catdir(@dirs), MakefilePL,
266             );
267 0 0 0     0 unless ( -f $buildpl or -f $makefilepl ) {
268 0         0 pop @dirs;
269 0         0 next;
270             }
271              
272             # This is a distroot
273 0         0 my $distroot = catpath( $v, catdir(@dirs), undef );
274 0         0 Cwd::chdir($distroot);
275 0         0 last;
276             }
277 0 0       0 unless ( in_distroot ) {
278 0         0 error "Failed to locate the distribution root";
279             }
280              
281             # Makefile.PL? Or Build.PL?
282 0 0       0 my $BUILD_SYSTEM = has_buildpl ? 'build' : has_makefilepl ? 'make' : '';
    0          
283 0 0       0 if ( $BUILD_SYSTEM eq 'build' ) {
284             # Because Module::Build always runs with warnings on,
285             # pler will as well when you use a Build.PL
286 0 0       0 unless ( grep { $_ eq '-w' } @SWITCHES ) {
  0         0  
287 0         0 push @SWITCHES, '-w';
288             }
289             }
290              
291             # If needed, regenerate the Makefile or Build file
292             # Currently we do not remember Makefile.PL or Build.PL params
293 0 0       0 if ( $BUILD_SYSTEM eq 'make' ) {
    0          
294 0 0 0     0 if ( needs_makefile or (old_makefile and ! OVERWRITE_OK) ) {
      0        
295 0         0 run( join ' ', perl, MakefilePL );
296             }
297             } elsif ( $BUILD_SYSTEM eq 'build' ) {
298 0 0 0     0 if ( needs_build or old_build ) {
299 0         0 run( join ' ', perl, BuildPL );
300             }
301             }
302              
303             # Locate the test script to run
304 0 0       0 if ( $script =~ /\.t$/ ) {
305             # EITHER
306             # 1. They tab-completed the script relative to the original directory (most likely)
307             # OR
308             # 2. They typed the entire name of the test script
309 0         0 my $tab_completed = File::Spec->catfile( $orig, $script );
310 0 0       0 if ( -f $tab_completed ) {
311 0 0       0 if ( $orig eq $ENV{PWD} ) {
312 0         0 $script = $script; # Included for clarity
313             } else {
314 0         0 $script = File::Spec->abs2rel( $tab_completed, $ENV{PWD} );
315             }
316             }
317              
318             } else {
319             # Get the list of possible tests
320 0 0       0 my @directory = ( 't', has_xt ? 'xt' : () );
321 0         0 my @possible = File::Find::Rule->name('*.t')->file->in(@directory);
322              
323             # Filter by the search terms to find matching tests
324 0         0 my $matches = filter(
325             [ $script, @ARGV ],
326             [ @possible ],
327             );
328 0 0       0 unless ( @$matches ) {
329 0         0 error "No tests match '$script'";
330             }
331 0 0       0 if ( @$matches > 1 ) {
332 0         0 error(
333             "More than one possible test",
334 0         0 map { " $_" } sort @$matches,
335             );
336             }
337 0         0 $script = $matches->[0];
338              
339             # Localize the path
340 0         0 $script = File::Spec->catfile( split /\//, $script );
341             }
342 0 0       0 unless ( -f $script ) {
343 0         0 error "Test script '$script' does not exist";
344             }
345              
346             # Rerun make or Build if needed
347 0 0       0 if ( $BUILD_SYSTEM eq 'make' ) {
    0          
348             # Do NOT run make if there is no Makefile.PL, because it likely means
349             # there is a hand-written Makefile and NOT one derived from Makefile.PL,
350             # and we have no idea what functionality we might trigger.
351 0 0 0     0 if ( in_distroot and has_makefile and has_makefilepl ) {
      0        
352 0         0 run( make );
353             }
354             } elsif ( $BUILD_SYSTEM eq 'build' ) {
355 0 0 0     0 if ( in_distroot and has_build and has_buildpl ) {
      0        
356 0         0 run( Build );
357             }
358             }
359              
360             # Passing includes via -I params is not good enough
361             # because you can't subshell them, and it's also not
362             # how MakeMaker does it anyway.
363             # We need to hack/extend PERL5LIB instead.
364 0         0 my $path_sep = $Config{path_sep};
365 0         0 my @PERL5LIB = ();
366              
367             # Build the command to execute
368 0         0 my @flags = @SWITCHES;
369 0 0       0 if ( has_blib ) {
    0          
370 0 0       0 if ( has_inc ) {
371 0         0 push @PERL5LIB, inc;
372             }
373 0         0 push @PERL5LIB, File::Spec->catdir(
374             blib, 'lib',
375             );
376 0         0 push @PERL5LIB, File::Spec->catdir(
377             blib, 'arch',
378             );
379             } elsif ( has_lib ) {
380 0         0 push @PERL5LIB, lib;
381             }
382              
383             # Absolutify the PERL5LIB elements so they will survive
384             # the test script changing it's CWD. This was added to
385             # deal with the path-shifting of the Padre tests.
386 0         0 @PERL5LIB = map {
387 0         0 File::Spec->rel2abs($_)
388             } @PERL5LIB;
389              
390             # Hand off to the perl debugger
391 0 0       0 unless ( pler->is_verbose ) {
392 0         0 message( "# Debugging $script...\n" );
393             }
394 0         0 my @cmd = ( perl, @flags, '-d', $script );
395 0 0       0 local $ENV{PERL5LIB} = defined($ENV{PERL5LIB})
396             ? join( $path_sep, @PERL5LIB, $ENV{PERL5LIB} )
397             : join( $path_sep, @PERL5LIB );
398 0         0 handoff( @cmd );
399             }
400              
401             # Encapsulates the smart filtering as a function
402             sub filter {
403 3     3 0 294 my $terms = shift;
404 3         23 my $possible = shift;
405 3         7 my @matches = @$possible;
406              
407 3         9 while ( @$terms ) {
408 3         6 my $term = shift @$terms;
409              
410 3 50       17 if ( ref $term eq 'Regexp' ) {
    100          
411             # If the term is a regexp apply it directly
412 0         0 @matches = grep { $_ =~ $term } @matches;
  0         0  
413             } elsif ( $term =~ /^[1-9]\d*$/ ) {
414             # If the search is a pure integer (without leading
415             # zeros) attempt a specialised numeric filter.
416 2         5 @matches = grep { /\b0*${term}[^0-9]/ } @matches;
  6         67  
417             } else {
418             # Otherwise treat it as a naive string match
419 1         4 $term = quotemeta $term;
420 1         2 @matches = grep { /$term/i } @matches;
  3         23  
421             }
422             }
423              
424 3         19 return \@matches;
425             }
426              
427 0     0 0   sub help { print <<'END_HELP'; exit(0); }
  0            
428             Usage:
429             pler [options] [file/pattern]
430              
431             Options:
432             -V Print the pler version
433             -h, --help Display this help
434             -w Run test with the -w warnings flag
435             END_HELP
436              
437             1;
438              
439             =pod
440              
441             =head1 NAME
442              
443             pler - The DWIM Perl Debugger
444              
445             =head1 DESCRIPTION
446              
447             B is a small script which provides a sanity layer for debugging
448             test scripts in Perl distributions.
449              
450             While L has proven itself to be a highly useful program for
451             manually running one or more groups of scripts in a distribution,
452             what we also need is something that provides a similar level of
453             intelligence in a debugging context.
454              
455             B checks that the environment is sound, runs some cleanup tasks
456             if needed, makes sure you are in the right directory, and then hands off
457             to the perl debugger as normal.
458              
459             =head1 TO DO
460              
461             - Tweak some small terminal related issues on Win32
462              
463             =head1 SUPPORT
464              
465             All bugs should be filed via the bug tracker at
466              
467             L
468              
469             For other issues, or commercial enhancement and support, contact the author
470              
471             =head1 AUTHOR
472              
473             Adam Kennedy Eadamk@cpan.orgE
474              
475             =head1 SEE ALSO
476              
477             L, L
478              
479             =head1 COPYRIGHT
480              
481             Copyright 2006 - 2010 Adam Kennedy.
482              
483             This program is free software; you can redistribute
484             it and/or modify it under the same terms as Perl itself.
485              
486             The full text of the license can be found in the
487             LICENSE file included with this module.
488              
489             =cut