File Coverage

blib/lib/AnyEvent/Open3/Simple.pm
Criterion Covered Total %
statement 109 143 76.2
branch 38 58 65.5
condition 9 13 69.2
subroutine 19 21 90.4
pod 1 2 50.0
total 176 237 74.2


line stmt bran cond sub pod time code
1             package AnyEvent::Open3::Simple;
2              
3 15     15   1247654 use strict;
  15         175  
  15         474  
4 15     15   83 use warnings;
  15         28  
  15         363  
5 15     15   430 use 5.006;
  15         111  
6 15     15   104 use warnings::register;
  15         32  
  15         2312  
7 15     15   7289 use IPC::Open3 qw( open3 );
  15         56923  
  15         904  
8 15     15   112 use Scalar::Util qw( reftype );
  15         30  
  15         664  
9 15     15   646 use Symbol qw( gensym );
  15         913  
  15         551  
10 15     15   6699 use AnyEvent::Open3::Simple::Process;
  15         44  
  15         499  
11 15     15   96 use Carp qw( croak );
  15         30  
  15         658  
12 15     15   10434 use File::Temp ();
  15         271825  
  15         521  
13 15     15   108 use constant _is_native_win32 => $^O eq 'MSWin32';
  15         41  
  15         1060  
14 15     15   109 use constant _detect => _is_native_win32() ? 'idle' : 'child';
  15         43  
  15         26378  
15              
16             # ABSTRACT: Interface to open3 under AnyEvent
17             our $VERSION = '0.90'; # VERSION
18              
19              
20             sub new
21             {
22 19     20 0 48273 my $default_handler = sub { };
        19      
23 19         58 my $class = shift;
24 19 100 100     279 my $args = (reftype($_[0]) || '') eq 'HASH' ? shift : { @_ };
25 19         47 my %self;
26 19 100       550 croak "stdin passed into AnyEvent::Open3::Simple->new no longer supported" if $args->{stdin};
27 18 50       105 croak "raw passed into AnyEvent::Open::Simple->new no longer supported" if $args->{raw};
28 18   66     410 $self{$_} = $args->{$_} || $default_handler for qw( on_stdout on_stderr on_start on_exit on_signal on_fail on_error on_success );
29             $self{impl} = $args->{implementation}
30             || $ENV{ANYEVENT_OPEN3_SIMPLE}
31 18   50     185 || _detect();
32 18 50       192 croak "unknown implementation $self{impl}" unless $self{impl} =~ /^(idle|child|mojo)$/;
33             $self{impl} = _detect()
34 18 0 33     76 if $self{impl} eq 'mojo' && do { require Mojo::Reactor; Mojo::Reactor->detect eq 'Mojo::Reactor::EV' };
  0         0  
  0         0  
35 18         113 bless \%self, $class;
36             }
37              
38              
39             sub run
40             {
41 19 100   19 1 19353 croak "run method requires at least one argument"
42             unless @_ >= 2;
43              
44 18 100   14   116 my $proc_user = (ref $_[-1] eq 'CODE' ? pop : sub {});
45              
46 18         40 my $stdin;
47 18 100       68 $stdin = pop if ref $_[-1];
48              
49 18         74 my($self, $program, @arguments) = @_;
50              
51 18         45 my($child_stdin, $child_stdout, $child_stderr);
52 18         100 $child_stderr = gensym;
53              
54 18         465 local *TEMP;
55 18 100       78 if(defined $stdin)
56             {
57 2         19 my $file = File::Temp->new;
58 2         1408 $file->autoflush(1);
59             $file->print(
60             ref($stdin) eq 'ARRAY'
61 2 100       185 ? join("\n", @{ $stdin })
  1         9  
62             : $$stdin
63             );
64 2         166 $file->seek(0,0);
65 2         50 open TEMP, '<&=', $file; ## no critic
66 2         80 $child_stdin = '<&TEMP';
67             }
68              
69 18 50       865 if($self->{impl} =~ /^(child|idle)$/)
    0          
70             {
71 18         1241 require AnyEvent;
72 18         5843 AnyEvent::detect();
73 18 50       1537 require AnyEvent::Open3::Simple::Idle if $self->{impl} eq 'idle';
74             }
75             elsif($self->{impl} eq 'mojo')
76             {
77 0         0 require Mojo::Reactor;
78 0         0 require Mojo::IOLoop;
79 0         0 require AnyEvent::Open3::Simple::Mojo;
80             }
81              
82 18         42 my $pid = eval { open3 $child_stdin, $child_stdout, $child_stderr, $program, @arguments };
  18         102  
83              
84 18 100       84221 if(my $error = $@)
85             {
86 2         38 $self->{on_error}->($error, $program, @arguments);
87 2         291 return;
88             }
89              
90 16         650 my $proc = AnyEvent::Open3::Simple::Process->new($pid, $child_stdin);
91 16         285 $proc_user->($proc);
92              
93 16         335 $self->{on_start}->($proc, $program, @arguments);
94              
95 16         149 my $watcher_stdout;
96             my $watcher_stderr;
97              
98             my $stdout_callback = sub {
99 11     11   54684 my $input = <$child_stdout>;
100 11 100       171 return unless defined $input;
101 4         53 $input =~ s/(\015?\012|\015)$//;
102 4         23 my $ref = $self->{on_stdout};
103 4 100       45 ref($ref) eq 'ARRAY' ? push @$ref, $input : $ref->($proc, $input);
104 16         512 };
105              
106             my $stderr_callback = sub {
107 12     12   8525 my $input = <$child_stderr>;
108 12 100       141 return unless defined $input;
109 2         49 $input =~ s/(\015?\012|\015)$//;
110 2         25 my $ref = $self->{on_stderr};
111 2 100       36 ref($ref) eq 'ARRAY' ? push @$ref, $input : $ref->($proc, $input);
112 16         262 };
113              
114 16 50       429 if(!_is_native_win32() && $self->{impl} =~ /^(idle|child)$/)
115             {
116 16         607 $watcher_stdout = AnyEvent->io(
117             fh => $child_stdout,
118             poll => 'r',
119             cb => $stdout_callback,
120             ) unless _is_native_win32();
121              
122 16         876 $watcher_stderr = AnyEvent->io(
123             fh => $child_stderr,
124             poll => 'r',
125             cb => $stderr_callback,
126             ) unless _is_native_win32();
127             }
128              
129 16         269 my $watcher_child;
130              
131             my $end_cb = sub {
132             #my($pid, $status) = @_;
133 13     13   5488 my $status = $_[1];
134 13         93 my($exit_value, $signal) = ($status >> 8, $status & 127);
135              
136 13         161 $proc->close;
137              
138             # make sure we consume any stdout and stderr which hasn't
139             # been consumed yet. This seems to make on_out.t work on
140             # cygwin
141 13 50       134 if($self->{raw})
142             {
143 0         0 local $/;
144 0         0 $self->{on_stdout}->($proc, scalar <$child_stdout>);
145 0         0 $self->{on_stderr}->($proc, scalar <$child_stderr>);
146             }
147             else
148             {
149 13         271 while(!eof $child_stdout)
150             {
151 0         0 my $input = <$child_stdout>;
152 0 0       0 last unless defined $input;
153 0         0 $input =~ s/(\015?\012|\015)$//;
154 0         0 my $ref = $self->{on_stdout};
155 0 0       0 ref($ref) eq 'ARRAY' ? push @$ref, $input : $ref->($proc, $input);
156             }
157              
158 13         217 while(!eof $child_stderr)
159             {
160 0         0 my $input = <$child_stderr>;
161 0 0       0 last unless defined $input;
162 0         0 $input =~ s/(\015?\012|\015)$//;
163 0         0 my $ref = $self->{on_stderr};
164 0 0       0 ref($ref) eq 'ARRAY' ? push @$ref, $input : $ref->($proc, $input);
165             }
166             }
167              
168 13         152 $self->{on_exit}->($proc, $exit_value, $signal);
169 13 100       669 $self->{on_signal}->($proc, $signal) if $signal > 0;
170 13 100       78 $self->{on_fail}->($proc, $exit_value) if $exit_value > 0;
171 13 100 100     236 $self->{on_success}->($proc) if $signal == 0 && $exit_value == 0;
172 13         304 undef $watcher_stdout;
173 13         173 undef $watcher_stderr;
174 13         31 undef $watcher_child;
175 13         947 undef $proc;
176 16         359 };
177              
178 16 50       187 if($self->{impl} eq 'mojo')
    50          
179             {
180 0         0 my($selout, $selerr);
181              
182 0         0 if(_is_native_win32())
183             {
184             require IO::Select;
185             $selout = IO::Select->new($child_stdout);
186             $selerr = IO::Select->new($child_stderr);
187             }
188              
189 0         0 my $reactor = Mojo::IOLoop->singleton->reactor;
190 0         0 my $id;
191             $id = Mojo::IOLoop->recurring(0.25 => sub {
192             AnyEvent::Open3::Simple::Mojo::_watcher($pid, sub {
193 0         0 $end_cb->(@_);
194 0         0 Mojo::IOLoop->remove($id);
195 0         0 if(_is_native_win32())
196             {
197             $stdout_callback->() if $selout->can_read(0);
198             $stderr_callback->() if $selerr->can_read(0);
199             }
200             else
201             {
202 0         0 $reactor->remove($child_stdout);
203 0         0 $reactor->remove($child_stderr);
204             }
205 0     0   0 });
206 0         0 });
207              
208             }
209             elsif($self->{impl} eq 'idle')
210             {
211 0         0 my($selout, $selerr);
212              
213 0         0 if(_is_native_win32())
214             {
215             require IO::Select;
216             $selout = IO::Select->new($child_stdout);
217             $selerr = IO::Select->new($child_stderr);
218             }
219              
220             $watcher_child = AnyEvent->idle(cb => sub {
221 0     0   0 if(_is_native_win32())
222             {
223             $stdout_callback->() if $selout->can_read(0);
224             $stderr_callback->() if $selerr->can_read(0);
225             }
226 0         0 AnyEvent::Open3::Simple::Idle::_watcher($pid, $end_cb);
227 0         0 });
228             }
229             else
230             {
231 16         241 $watcher_child = AnyEvent->child(
232             pid => $pid,
233             cb => $end_cb,
234             );
235             }
236              
237 16         1058 $self;
238             }
239              
240             1;
241              
242             __END__