bring Test::Harness up to 3.06
[p5sagit/p5-mst-13.2.git] / lib / TAP / Parser / Source / Perl.pm
CommitLineData
b965d173 1package TAP::Parser::Source::Perl;
2
3use strict;
4use Config;
5use vars qw($VERSION @ISA);
6
7use constant IS_WIN32 => ( $^O =~ /^(MS)?Win32$/ );
8use constant IS_VMS => ( $^O eq 'VMS' );
9
10use TAP::Parser::Source;
11@ISA = 'TAP::Parser::Source';
12
13=head1 NAME
14
15TAP::Parser::Source::Perl - Stream Perl output
16
17=head1 VERSION
18
69f36734 19Version 3.06
b965d173 20
21=cut
22
69f36734 23$VERSION = '3.06';
b965d173 24
25=head1 DESCRIPTION
26
27Takes a filename and hopefully returns a stream from it. The filename should
28be the name of a Perl program.
29
30Note that this is a subclass of L<TAP::Parser::Source>. See that module for
31more methods.
32
33=head1 SYNOPSIS
34
35 use TAP::Parser::Source::Perl;
36 my $perl = TAP::Parser::Source::Perl->new;
37 my $stream = $perl->source( [ $filename, @args ] )->get_stream;
38
39=head1 METHODS
40
41=head2 Class Methods
42
43=head3 C<new>
44
45 my $perl = TAP::Parser::Source::Perl->new;
46
47Returns a new C<TAP::Parser::Source::Perl> object.
48
49=head2 Instance Methods
50
51=head3 C<source>
52
53Getter/setter the name of the test program and any arguments it requires.
54
55 my ($filename, @args) = @{ $perl->source };
56 $perl->source( [ $filename, @args ] );
57
58=cut
59
60sub source {
61 my $self = shift;
62 $self->_croak("Cannot find ($_[0][0])")
63 if @_ && !-f $_[0][0];
64 return $self->SUPER::source(@_);
65}
66
67=head3 C<switches>
68
69 my $switches = $perl->switches;
70 my @switches = $perl->switches;
71 $perl->switches( \@switches );
72
73Getter/setter for the additional switches to pass to the perl executable. One
74common switch would be to set an include directory:
75
76 $perl->switches( ['-Ilib'] );
77
78=cut
79
80sub switches {
81 my $self = shift;
82 unless (@_) {
83 return wantarray ? @{ $self->{switches} } : $self->{switches};
84 }
85 my $switches = shift;
86 $self->{switches} = [@$switches]; # force a copy
87 return $self;
88}
89
90##############################################################################
91
92=head3 C<get_stream>
93
94 my $stream = $source->get_stream;
95
96Returns a stream of the output generated by executing C<source>.
97
98=cut
99
100sub get_stream {
101 my $self = shift;
102
103 my @extra_libs;
104
105 my @switches = $self->_switches;
106 my $path_sep = $Config{path_sep};
107 my $path_pat = qr{$path_sep};
108
109 # Nasty kludge. It might be nicer if we got the libs separately
110 # although at least this way we find any -I switches that were
111 # supplied other then as explicit libs.
112 # We filter out any names containing colons because they will break
113 # PERL5LIB
114 my @libs;
115 for ( grep { $_ !~ $path_pat } @switches ) {
116 push @libs, $1 if / ^ ['"]? -I (.*?) ['"]? $ /x;
117 }
118
119 my $previous = $ENV{PERL5LIB};
120 if ($previous) {
121 push @libs, split( $path_pat, $previous );
122 }
123
124 my $setup = sub {
125 if (@libs) {
126 $ENV{PERL5LIB} = join( $path_sep, @libs );
127 }
128 };
129
130 # Cargo culted from comments seen elsewhere about VMS / environment
131 # variables. I don't know if this is actually necessary.
132 my $teardown = sub {
133 if ($previous) {
134 $ENV{PERL5LIB} = $previous;
135 }
136 else {
137 delete $ENV{PERL5LIB};
138 }
139 };
140
141 # Taint mode ignores environment variables so we must retranslate
142 # PERL5LIB as -I switches and place PERL5OPT on the command line
143 # in order that it be seen.
144 if ( grep { $_ eq "-T" } @switches ) {
145 push @switches,
146 $self->_libs2switches(
147 split $path_pat,
148 $ENV{PERL5LIB} || $ENV{PERLLIB} || ''
149 );
150
151 push @switches, $ENV{PERL5OPT} || ();
152 }
153
154 my @command = $self->_get_command_for_switches(@switches)
155 or $self->_croak("No command found!");
156
157 return TAP::Parser::Iterator->new(
158 { command => \@command,
159 merge => $self->merge,
160 setup => $setup,
161 teardown => $teardown,
162 }
163 );
164}
165
166sub _get_command_for_switches {
167 my $self = shift;
168 my @switches = @_;
169 my ( $file, @args ) = @{ $self->source };
170 my $command = $self->_get_perl;
171
172 $file = qq["$file"] if ( $file =~ /\s/ ) && ( $file !~ /^".*"$/ );
173 my @command = ( $command, @switches, $file, @args );
174 return @command;
175}
176
177sub _get_command {
178 my $self = shift;
179 return $self->_get_command_for_switches( $self->_switches );
180}
181
182sub _libs2switches {
183 my $self = shift;
184 return map {"-I$_"} grep {$_} @_;
185}
186
187=head3 C<shebang>
188
189Get the shebang line for a script file.
190
191 my $shebang = TAP::Parser::Source::Perl->shebang( $some_script );
192
193May be called as a class method
194
195=cut
196
197{
198
199 # Global shebang cache.
200 my %shebang_for;
201
202 sub _read_shebang {
203 my $file = shift;
204 local *TEST;
205 my $shebang;
206 if ( open( TEST, $file ) ) {
207 $shebang = <TEST>;
208 close(TEST) or print "Can't close $file. $!\n";
209 }
210 else {
211 print "Can't open $file. $!\n";
212 }
213 return $shebang;
214 }
215
216 sub shebang {
217 my ( $class, $file ) = @_;
218 unless ( exists $shebang_for{$file} ) {
219 $shebang_for{$file} = _read_shebang($file);
220 }
221 return $shebang_for{$file};
222 }
223}
224
225=head3 C<get_taint>
226
227Decode any taint switches from a Perl shebang line.
228
229 # $taint will be 't'
230 my $taint = TAP::Parser::Source::Perl->get_taint( '#!/usr/bin/perl -t' );
231
232 # $untaint will be undefined
233 my $untaint = TAP::Parser::Source::Perl->get_taint( '#!/usr/bin/perl' );
234
235=cut
236
237sub get_taint {
238 my ( $class, $shebang ) = @_;
239 return
240 unless defined $shebang
241 && $shebang =~ /^#!.*\bperl.*\s-\w*([Tt]+)/;
242 return $1;
243}
244
245sub _switches {
246 my $self = shift;
247 my ( $file, @args ) = @{ $self->source };
248 my @switches = (
249 $self->switches,
250 );
251
252 my $shebang = $self->shebang($file);
253 return unless defined $shebang;
254
255 my $taint = $self->get_taint($shebang);
256 push @switches, "-$taint" if defined $taint;
257
258 # Quote the argument if there's any whitespace in it, or if
259 # we're VMS, since VMS requires all parms quoted. Also, don't quote
260 # it if it's already quoted.
261 for (@switches) {
262 $_ = qq["$_"] if ( ( /\s/ || IS_VMS ) && !/^".*"$/ );
263 }
264
265 my %found_switch = map { $_ => 0 } @switches;
266
267 # remove duplicate switches
268 @switches
269 = grep { defined $_ && $_ ne '' && !$found_switch{$_}++ } @switches;
270 return @switches;
271}
272
273sub _get_perl {
274 my $proto = shift;
69f36734 275 return $ENV{HARNESS_PERL} if defined $ENV{HARNESS_PERL};
b965d173 276 return Win32::GetShortPathName($^X) if IS_WIN32;
277 return $^X;
278}
279
2801;