File Coverage

blib/lib/POE/Component/CPANPLUS/YACSmoke.pm
Criterion Covered Total %
statement 201 386 52.0
branch 70 182 38.4
condition 16 82 19.5
subroutine 21 45 46.6
pod 10 10 100.0
total 318 705 45.1


line stmt bran cond sub pod time code
1             package POE::Component::CPANPLUS::YACSmoke;
2             $POE::Component::CPANPLUS::YACSmoke::VERSION = '1.64';
3             #ABSTRACT: Bringing the power of POE to CPAN smoke testing.
4              
5 11     11   14494 use strict;
  11         37  
  11         352  
6 11     11   66 use warnings;
  11         145  
  11         450  
7 11     11   71 use POE qw(Wheel::Run);
  11         29  
  11         147  
8 11     11   223872 use Storable;
  11         30163  
  11         721  
9 11     11   92 use Digest::MD5 qw(md5_hex);
  11         27  
  11         1033  
10              
11             my $GOT_KILLFAM;
12             my $GOT_PTY;
13              
14             BEGIN {
15 11     11   47 $GOT_KILLFAM = 0;
16 11         27 eval {
17 11         1623 require Proc::ProcessTable;
18 0         0 $GOT_KILLFAM = 1;
19             };
20 11         52 $GOT_PTY = 0;
21 11         36 eval {
22 11         49 require IO::Pty;
23 11         42562 $GOT_PTY = 1;
24             };
25             }
26              
27             sub spawn {
28 11     11 1 9302 my $package = shift;
29 11         62 my %opts = @_;
30 11         104 $opts{lc $_} = delete $opts{$_} for keys %opts;
31 11         38 my $options = delete $opts{options};
32              
33 11 50       67 if ( $^O eq 'MSWin32' ) {
34 0         0 eval { require Win32; };
  0         0  
35 0 0       0 if ($@) { die "Win32 but failed to load:\n$@" }
  0         0  
36 0         0 eval { require Win32::Job; };
  0         0  
37 0 0       0 if ($@) { die "Win32::Job but failed to load:\n$@" }
  0         0  
38 0         0 eval { require Win32::Process; };
  0         0  
39 0 0       0 if ($@) { die "Win32::Process but failed to load:\n$@" }
  0         0  
40             }
41              
42 11         37 my $self = bless \%opts, $package;
43 11 50       277 $self->{session_id} = POE::Session->create(
44             object_states => [
45             $self => { shutdown => '_shutdown',
46             submit => '_command',
47             push => '_command',
48             unshift => '_command',
49             recent => '_command',
50             check => '_command',
51             indices => '_command',
52             author => '_command',
53             flush => '_command',
54             'package' => '_command',
55             },
56             $self => [ qw(_start _spawn_wheel _wheel_error _wheel_closed _wheel_stdout _wheel_stderr _wheel_idle _wheel_kill _sig_child _sig_handle) ],
57             ],
58             heap => $self,
59             ( ref($options) eq 'HASH' ? ( options => $options ) : () ),
60             )->ID();
61 11         2350 return $self;
62             }
63              
64             sub session_id {
65 0     0 1 0 return $_[0]->{session_id};
66             }
67              
68             sub pending_jobs {
69 2     2 1 7 return @{ $_[0]->{job_queue} };
  2         14  
70             }
71              
72             sub current_job {
73 0     0 1 0 my $self = shift;
74 0 0       0 return unless $self->{_current_job};
75 0         0 my $item = Storable::dclone( $self->{_current_job} );
76 0         0 return $item;
77             }
78              
79             sub current_log {
80 0     0 1 0 my $self = shift;
81 0 0       0 return unless $self->{_wheel_log};
82 0         0 my $item = Storable::dclone( $self->{_wheel_log} );
83 0         0 return $item;
84             }
85              
86             sub pause_queue {
87 1     1 1 719 my $self = shift;
88 1         9 $self->{paused} = 1;
89             }
90              
91             sub resume_queue {
92 1     1 1 4 my $self = shift;
93 1         5 my $pause = delete $self->{paused};
94 1 50       11 $poe_kernel->post( $self->{session_id}, '_spawn_wheel' ) if $pause;
95             }
96              
97             sub paused {
98 3     3 1 5004032 return $_[0]->{paused};
99             }
100              
101             sub statistics {
102 1     1 1 6597 my $self = shift;
103 1         5 my @stats;
104 1         14 push @stats, $self->{stats}->{$_} for qw(started totaljobs avg_run min_run max_run);
105 1 50       11 return @stats if wantarray;
106 0         0 return \@stats;
107             }
108              
109             sub shutdown {
110 0     0 1 0 my $self = shift;
111 0         0 $poe_kernel->post( $self->{session_id}, 'shutdown' );
112             }
113              
114             sub _start {
115 11     11   6271 my ($kernel,$self) = @_[KERNEL,OBJECT];
116 11         71 $kernel->sig( 'HUP', '_sig_handle' );
117 11         773 $self->{session_id} = $_[SESSION]->ID();
118 11 50       84 if ( $self->{alias} ) {
119 11         68 $kernel->alias_set( $self->{alias} );
120             } else {
121 0         0 $kernel->refcount_increment( $self->{session_id} => __PACKAGE__ );
122             }
123 11         575 $self->{job_queue} = [ ];
124 11 50       58 $self->{idle} = 600 unless $self->{idle};
125 11 50       74 $self->{timeout} = 3600 unless $self->{timeout};
126             $self->{stats} = {
127 11         123 started => time(),
128             totaljobs => 0,
129             avg_run => 0,
130             min_run => 0,
131             max_run => 0,
132             _sum => 0,
133             };
134 11 50       55 $ENV{APPDATA} = $self->{appdata} if $self->{appdata};
135 11         51 undef;
136             }
137              
138             sub _sig_handle {
139 1     1   242 $poe_kernel->sig_handled();
140             }
141              
142             sub _shutdown {
143 11     11   9151 my ($kernel,$self) = @_[KERNEL,OBJECT];
144 11         92 $kernel->sig( 'HUP' );
145 11         995 $kernel->sig( 'KILL' );
146 11         691 $kernel->alias_remove( $_ ) for $kernel->alias_list();
147 11 50       1393 $kernel->refcount_decrement( $self->{session_id} => __PACKAGE__ ) unless $self->{alias};
148 11         38 $kernel->refcount_decrement( $_->{session}, __PACKAGE__ ) for @{ $self->{job_queue} };
  11         76  
149 11         56 $self->{_shutdown} = 1;
150 11         51 undef;
151             }
152              
153             sub _command {
154 11     11   16914 my ($kernel,$self,$state,$sender) = @_[KERNEL,OBJECT,STATE,SENDER];
155 11 50       59 return if $self->{_shutdown};
156 11         32 my $args;
157 11 50       73 if ( ref( $_[ARG0] ) eq 'HASH' ) {
158 11         31 $args = { %{ $_[ARG0] } };
  11         66  
159             } else {
160 0         0 $args = { @_[ARG0..$#_] };
161             }
162              
163 11 100       53 $state = 'push' if $state eq 'submit';
164              
165 11         30 $args->{lc $_} = delete $args->{$_} for grep { $_ !~ /^_/ } keys %{ $args };
  43         202  
  11         43  
166              
167 11 50       55 my $ref = $args->{session} ? $kernel->alias_resolve( $args->{session} ) : $sender;
168 11         49 $args->{session} = $ref->ID();
169              
170 11 50 66     127 if ( !$args->{module} and $state !~ /^(recent|check|indices|package|author|flush)$/i ) {
171 0         0 warn "No 'module' specified for $state";
172 0         0 return;
173             }
174              
175 11 50       46 unless ( $args->{event} ) {
176 0         0 warn "No 'event' specified for $state";
177 0         0 return;
178             }
179              
180 11 50 66     208 if ( $state =~ /^(package|author)$/ and !$args->{search} ) {
181 0         0 warn "No 'search' criteria specified for $state";
182 0         0 return;
183             }
184              
185 11         42 $args->{submitted} = time();
186              
187 11 100       173 if ( $state eq 'recent' ) {
    100          
    100          
    100          
    100          
    100          
188 1 50       4 if ( $^O eq 'MSWin32' ) {
189 0         0 $args->{program} = \&_recent_modules;
190 0   0     0 $args->{program_args} = [ $args->{perl} || $self->{perl} || $^X ];
191             }
192             else {
193 1   0     4 my $perl = $args->{perl} || $self->{perl} || $^X;
194 1         2 my $code = 'my $smoke = CPANPLUS::YACSmoke->new(); print "$_\n" for $smoke->_download_list();';
195 1         3 $args->{program} = [ $perl, '-MCPANPLUS::YACSmoke', '-e', $code ];
196             }
197             }
198             elsif ( $state eq 'check' ) {
199 1 50       4 if ( $^O eq 'MSWin32' ) {
200 0         0 $args->{program} = \&_check_yacsmoke;
201 0   0     0 $args->{program_args} = [ $args->{perl} || $self->{perl} || $^X ];
202             }
203             else {
204 1   33     6 my $perl = $args->{perl} || $self->{perl} || $^X;
205 1         3 $args->{program} = [ $perl, '-MCPANPLUS::YACSmoke', '-e', 1 ];
206             }
207 1         5 $args->{debug} = 1;
208             }
209             elsif ( $state eq 'indices' ) {
210 1 50       9 $args->{prioritise} = 0 unless $args->{prioritise};
211 1 50       6 if ( $^O eq 'MSWin32' ) {
212 0         0 $args->{program} = \&_reload_indices;
213 0   0     0 $args->{program_args} = [ $args->{perl} || $self->{perl} || $^X ];
214             }
215             else {
216 1   0     4 my $perl = $args->{perl} || $self->{perl} || $^X;
217 1         2 my $code = 'CPANPLUS::Backend->new()->reload_indices( update_source => 1 );';
218 1         4 $args->{program} = [ $perl, '-MCPANPLUS::Backend', '-e', $code ];
219             }
220             }
221             elsif ( $state eq 'author' ) {
222 1 50       8 if ( $^O eq 'MSWin32' ) {
223 0         0 $args->{program} = \&_author_search;
224 0   0     0 $args->{program_args} = [ $args->{perl} || $self->{perl} || $^X, $args->{type} || 'cpanid', $args->{search} ];
      0        
225             }
226             else {
227 1   0     5 my $perl = $args->{perl} || $self->{perl} || $^X;
228 1         2 my $code = 'my $type = shift; my $search = shift; my $cb = CPANPLUS::Backend->new(); my %mods = map { $_->package() => 1 } map { $_->modules() } $cb->search( type => $type, allow => [ qr/$search/ ], verbose => 0 ); print qq{$_\n} for sort keys %mods;';
229 1   50     7 $args->{program} = [ $perl, '-MCPANPLUS::Backend', '-e', $code, $args->{type} || 'cpanid', $args->{search} ];
230             }
231             }
232             elsif ( $state eq 'package' ) {
233 1 50       10 if ( $^O eq 'MSWin32' ) {
234 0         0 $args->{program} = \&_package_search;
235 0   0     0 $args->{program_args} = [ $args->{perl} || $self->{perl} || $^X, $args->{type} || 'package', $args->{search} ];
      0        
236             }
237             else {
238 1   0     7 my $perl = $args->{perl} || $self->{perl} || $^X;
239 1         5 my $code = 'my $type = shift; my $search = shift; my $cb = CPANPLUS::Backend->new(); my %mods = map { $_->package() => 1 } $cb->search( type => $type, allow => [ qr/$search/ ], verbose => 0 ); print qq{$_\n} for sort keys %mods;';
240 1   50     15 $args->{program} = [ $perl, '-MCPANPLUS::Backend', '-e', $code, $args->{type} || 'package', $args->{search} ];
241             }
242             }
243             elsif ( $state eq 'flush' ) {
244 1 50       14 $args->{prioritise} = 0 unless $args->{prioritise};
245 1 50       10 if ( $^O eq 'MSWin32' ) {
246 0         0 $args->{program} = \&_flush;
247 0   0     0 $args->{program_args} = [ $args->{perl} || $self->{perl} || $^X, ( $args->{type} and $args->{type} eq 'all' ? 'all' : 'old' ) ];
      0        
248             }
249             else {
250 1   0     8 my $perl = $args->{perl} || $self->{perl} || $^X;
251 1         4 my $code = 'my $type = shift; my $smoke = CPANPLUS::YACSmoke->new(); $smoke->flush($type) if $smoke->can("flush");';
252 1   33     10 $args->{program} = [ $perl, '-MCPANPLUS::YACSmoke', '-e', $code, ( $args->{type} and $args->{type} eq 'all' ? 'all' : 'old' ) ];
253             }
254             }
255             else {
256 5 50       29 if ( $^O eq 'MSWin32' ) {
257 0         0 $args->{program} = \&_test_module;
258 0   0     0 $args->{program_args} = [ $args->{perl} || $self->{perl} || $^X, $args->{module} ];
259             }
260             else {
261 5   0     28 my $perl = $args->{perl} || $self->{perl} || $^X;
262 5         11 my $code = 'my $module = shift; my $smoke = CPANPLUS::YACSmoke->new(); $smoke->test($module);';
263 5         21 $args->{program} = [ $perl, '-MCPANPLUS::YACSmoke', '-e', $code, $args->{module} ];
264             }
265             }
266              
267 11         80 $kernel->refcount_increment( $args->{session}, __PACKAGE__ );
268              
269 11         498 $args->{cmd} = $state;
270              
271 11 100 66     138 if ( $state eq 'unshift' or $state eq 'recent' or $args->{prioritise} ) {
      66        
272 1         2 unshift @{ $self->{job_queue} }, $args;
  1         3  
273             }
274             else {
275 10         32 push @{ $self->{job_queue} }, $args;
  10         35  
276             }
277              
278 11         98 $kernel->yield( '_spawn_wheel' );
279              
280 11         990 undef;
281             }
282              
283             sub _sig_child {
284 11     11   9609 my ($kernel,$self,$thing,$pid,$status) = @_[KERNEL,OBJECT,ARG0..ARG2];
285 11         37 push @{ $self->{_wheel_log} }, "$thing $pid $status";
  11         84  
286 11 100       453 warn "$thing $pid $status\n" if $self->{debug};
287 11         92 $kernel->delay( '_wheel_idle' );
288 11         1093 delete $self->{_digests};
289 11         37 delete $self->{_loop_detect};
290 11         42 my $job = delete $self->{_current_job};
291 11         66 $job->{status} = $status;
292 11         36 my $log = delete $self->{_wheel_log};
293 11 100       155 if ( $job->{cmd} eq 'recent' ) {
    100          
294 1         4 pop @{ $log };
  1         5  
295 1         3 s/\x0D$// for @{ $log };
  1         6  
296 1         6 $job->{recent} = $log;
297             }
298             elsif ( $job->{cmd} =~ /^(package|author)$/ ) {
299 2         20 pop @{ $log };
  2         8  
300 2         6 s/\x0D$// for @{ $log };
  2         13  
301 2         8 @{ $job->{results} } = grep { $_ !~ /^\[/ } @{ $log };
  2         16  
  2         13  
  2         7  
302             }
303             else {
304 8         54 $job->{log} = $log;
305             }
306 11         122 $job->{end_time} = time();
307 11 100       74 unless ( $self->{debug} ) {
308 6         38 delete $job->{program};
309 6         23 delete $job->{program_args};
310             }
311             # Stats
312 11         42 my $run_time = $job->{end_time} - $job->{start_time};
313 11 50       73 $self->{stats}->{max_run} = $run_time if $run_time > $self->{stats}->{max_run};
314 11 50       65 $self->{stats}->{min_run} = $run_time if $self->{stats}->{min_run} == 0;
315 11 50       57 $self->{stats}->{min_run} = $run_time if $run_time < $self->{stats}->{min_run};
316 11         103 $self->{stats}->{_sum} += $run_time;
317 11         129 $self->{stats}->{totaljobs}++;
318 11         53 $self->{stats}->{avg_run} = $self->{stats}->{_sum} / $self->{stats}->{totaljobs};
319 11         47 $self->{debug} = delete $job->{global_debug};
320             #$ENV{APPDATA} = delete $job->{backup_env} if $job->{appdata};
321 11         80 $kernel->post( $job->{session}, $job->{event}, $job );
322 11         1518 $kernel->refcount_decrement( $job->{session}, __PACKAGE__ );
323 11         981 $kernel->yield( '_spawn_wheel' );
324 11         1351 $kernel->sig_handled();
325             }
326              
327             sub _spawn_wheel {
328 23     23   59726 my ($kernel,$self) = @_[KERNEL,OBJECT];
329 23 50       117 return if $self->{wheel};
330 23 50       98 return if $self->{_shutdown};
331 23 100       90 return if $self->{paused};
332 22         58 my $job = shift @{ $self->{job_queue} };
  22         77  
333 22 100       119 return unless $job;
334 11         40 my $backup_env;
335 11 50       51 if ( $job->{appdata} ) {
336 0         0 $backup_env = $ENV{APPDATA};
337 0         0 $ENV{APPDATA} = $job->{appdata};
338             }
339             $self->{wheel} = POE::Wheel::Run->new(
340             Program => $job->{program},
341             ProgramArgs => $job->{program_args},
342             StdoutEvent => '_wheel_stdout',
343             StderrEvent => '_wheel_stderr',
344             ErrorEvent => '_wheel_error',
345             CloseEvent => '_wheel_close',
346 11   33     221 ( $GOT_PTY and !$self->{no_pty} ? ( Conduit => 'pty-pipe' ) : () ),
347             );
348 11 50       83877 if ( $job->{appdata} ) {
349 0         0 delete $ENV{APPDATA};
350 0 0       0 $ENV{APPDATA} = $backup_env if $backup_env;
351             }
352 11 50       84 unless ( $self->{wheel} ) {
353 0         0 warn "Couldn\'t spawn a wheel for $job->{module}\n";
354 0         0 $kernel->refcount_decrement( $job->{session}, __PACKAGE__ );
355 0         0 return;
356             }
357 11 100       88 if ( defined $job->{debug} ) {
358 5         45 $job->{global_debug} = delete $self->{debug};
359 5         16 $self->{debug} = $job->{debug};
360             }
361 11         71 $self->{_wheel_log} = [ ];
362 11         74 $self->{_digests} = { };
363 11         63 $self->{_loop_detect} = 0;
364 11         77 $self->{_current_job} = $job;
365 11         132 $job->{PID} = $self->{wheel}->PID();
366 11         145 $job->{start_time} = time();
367 11         129 $kernel->sig_child( $job->{PID}, '_sig_child' );
368 11 100       3067 $kernel->delay( '_wheel_idle', 60 ) unless $job->{cmd} eq 'indices';
369 11         1858 undef;
370             }
371              
372             sub _wheel_error {
373 11     11   6468 $poe_kernel->delay( '_wheel_idle' );
374 11         1627 delete $_[OBJECT]->{wheel};
375 11         6531 undef;
376             }
377              
378             sub _wheel_closed {
379 0     0   0 $poe_kernel->delay( '_wheel_idle' );
380 0         0 delete $_[OBJECT]->{wheel};
381 0         0 undef;
382             }
383              
384             sub _wheel_stdout {
385 0     0   0 my ($self, $input, $wheel_id) = @_[OBJECT, ARG0, ARG1];
386 0         0 $self->{_wheel_time} = time();
387 0         0 push @{ $self->{_wheel_log} }, $input;
  0         0  
388 0 0       0 warn $input, "\n" if $self->{debug};
389 0 0       0 if ( $self->_detect_loop( $input ) ) {
390 0         0 $self->{_current_job}->{excess_kill} = 1;
391 0         0 $poe_kernel->yield( '_wheel_kill', 'Killing current run CPAN::Shell loop detected' );
392 0         0 return;
393             }
394 0         0 undef;
395             }
396              
397             sub _wheel_stderr {
398 10     10   6248 my ($self, $input, $wheel_id) = @_[OBJECT, ARG0, ARG1];
399 10         61 $self->{_wheel_time} = time();
400 10 0 33     103 if ( $^O eq 'MSWin32' and !$self->{_current_job}->{GRP_PID} and my ($pid) = $input =~ /(\d+)/ ) {
      33        
401 0         0 $self->{_current_job}->{GRP_PID} = $pid;
402 0 0       0 warn "Grp PID: $pid\n" if $self->{debug};
403 0         0 return;
404             }
405 10 50       90 push @{ $self->{_wheel_log} }, $input unless $self->{_current_job}->{cmd} eq 'recent';
  10         163  
406 10 100       9606 warn $input, "\n" if $self->{debug};
407 10 50       98 if ( $self->_detect_loop( $input ) ) {
408 0         0 $self->{_current_job}->{excess_kill} = 1;
409 0         0 $poe_kernel->yield( '_wheel_kill', 'Killing current run CPAN::Shell loop detected' );
410 0         0 return;
411             }
412 10         57 undef;
413             }
414              
415             sub _detect_loop {
416 10     10   43 my $self = shift;
417 10   50     75 my $input = shift || return;
418 10 50       58 return if $self->{_loop_detect};
419 10         99 my $digest = md5_hex( $input );
420 10         62 $self->{_digests}->{ $digest }++;
421 10 50       81 return unless ++$self->{_digests}->{ $digest } > 300;
422 0           return $self->{_loop_detect} = 1;
423             }
424              
425             sub _wheel_idle {
426 0     0     my ($kernel,$self) = @_[KERNEL,OBJECT];
427 0           my $now = time();
428 0 0         if ( $now - $self->{_wheel_time} >= $self->{idle} ) {
429 0           $self->{_current_job}->{idle_kill} = 1;
430 0           $kernel->yield( '_wheel_kill', 'Killing current run due to excessive idle' );
431 0           return;
432             }
433 0 0         if ( $now - $self->{_current_job}->{start_time} >= $self->{timeout} ) {
434 0           $self->{_current_job}->{excess_kill} = 1;
435 0           $kernel->yield( '_wheel_kill', 'Killing current run due to excessive run-time' );
436 0           return;
437             }
438 0           $kernel->delay( '_wheel_idle', 60 );
439 0           return;
440             }
441              
442             sub _wheel_kill {
443 0     0     my ($kernel,$self,$reason) = @_[KERNEL,OBJECT,ARG0];
444 0           push @{ $self->{_wheel_log} }, $reason;
  0            
445 0 0         warn $reason, "\n" if $self->{debug};
446 0 0 0       if ( $^O eq 'MSWin32' and $self->{wheel} ) {
447 0           my $grp_pid = $self->{_current_job}->{GRP_PID};
448 0 0         return unless $grp_pid;
449 0 0         warn Win32::FormatMessage( Win32::GetLastError() )
450             unless Win32::Process::KillProcess( $grp_pid, 0 );
451             }
452             else {
453 0 0         if ( !$self->{no_grp_kill} ) {
    0          
454 0 0         if ( $^O eq 'solaris' ) {
455 0 0         kill( 9, '-' . $self->{wheel}->PID() ) if $self->{wheel};
456             }
457             else {
458 0 0         $self->{wheel}->kill(-9) if $self->{wheel};
459             }
460             }
461             elsif ( $GOT_KILLFAM ) {
462 0 0         _kill_family( 9, $self->{wheel}->PID() ) if $self->{wheel};
463             }
464             else {
465 0 0         $self->{wheel}->kill(9) if $self->{wheel};
466             }
467             }
468 0           return;
469             }
470              
471             sub _check_yacsmoke {
472 0     0     my $perl = shift;
473 0           my $cmdline = $perl . q{ -MCPANPLUS::YACSmoke -e 1};
474 0 0         my $job = Win32::Job->new()
475             or die Win32::FormatMessage( Win32::GetLastError() );
476 0 0         my $pid = $job->spawn( $perl, $cmdline )
477             or die Win32::FormatMessage( Win32::GetLastError() );
478 0           warn $pid, "\n";
479 0     0     my $ok = $job->watch( sub { 0 }, 60 );
  0            
480 0           my $hashref = $job->status();
481 0           return $hashref->{$pid}->{exitcode};
482             }
483              
484             sub _test_module {
485 0     0     my $perl = shift;
486 0           my $module = shift;
487 0           my $cmdline = $perl . ' -MCPANPLUS::YACSmoke -e "my $module = shift; my $smoke = CPANPLUS::YACSmoke->new(); $smoke->test($module);" ' . $module;
488 0 0         my $job = Win32::Job->new()
489             or die Win32::FormatMessage( Win32::GetLastError() );
490 0 0         my $pid = $job->spawn( $perl, $cmdline )
491             or die Win32::FormatMessage( Win32::GetLastError() );
492 0           warn $pid, "\n";
493 0     0     my $ok = $job->watch( sub { 0 }, 60 );
  0            
494 0           my $hashref = $job->status();
495 0           return $hashref->{$pid}->{exitcode};
496             }
497              
498             sub _flush {
499 0     0     my $perl = shift;
500 0           my $type = shift;
501 0           my $cmdline = $perl . ' -MCPANPLUS::YACSmoke -e "my $type = shift; my $smoke = CPANPLUS::YACSmoke->new(); $smoke->flush($type) if $smoke->can(q{flush});" ' . $type;
502 0 0         my $job = Win32::Job->new()
503             or die Win32::FormatMessage( Win32::GetLastError() );
504 0 0         my $pid = $job->spawn( $perl, $cmdline )
505             or die Win32::FormatMessage( Win32::GetLastError() );
506 0           warn $pid, "\n";
507 0     0     my $ok = $job->watch( sub { 0 }, 60 );
  0            
508 0           my $hashref = $job->status();
509 0           return $hashref->{$pid}->{exitcode};
510             }
511             sub _recent_modules {
512 0     0     my $perl = shift;
513 0           my $cmdline = $perl . ' -MCPANPLUS::YACSmoke -e "my $smoke = CPANPLUS::YACSmoke->new();print qq{$_\n} for $smoke->{plugin}->download_list();"';
514 0 0         my $job = Win32::Job->new()
515             or die Win32::FormatMessage( Win32::GetLastError() );
516 0 0         my $pid = $job->spawn( $perl, $cmdline )
517             or die Win32::FormatMessage( Win32::GetLastError() );
518 0           warn $pid, "\n";
519 0     0     my $ok = $job->watch( sub { 0 }, 60 );
  0            
520 0           my $hashref = $job->status();
521 0           return $hashref->{$pid}->{exitcode};
522             }
523              
524             sub _reload_indices {
525 0     0     my $perl = shift;
526 0           my $cmdline = $perl . ' -MCPANPLUS::Backend -e "CPANPLUS::Backend->new()->reload_indices( update_source => 1 );"';
527 0 0         my $job = Win32::Job->new()
528             or die Win32::FormatMessage( Win32::GetLastError() );
529 0 0         my $pid = $job->spawn( $perl, $cmdline )
530             or die Win32::FormatMessage( Win32::GetLastError() );
531 0           warn $pid, "\n";
532 0     0     my $ok = $job->watch( sub { 0 }, 60 );
  0            
533 0           my $hashref = $job->status();
534 0           return $hashref->{$pid}->{exitcode};
535             }
536              
537             sub _author_search {
538 0     0     my $perl = shift;
539 0           my $type = shift;
540 0           my $search = shift;
541 0           my $cmdline = $perl . ' -MCPANPLUS::YACSmoke -e "my $type = shift; my $search = shift; my $cb = CPANPLUS::Backend->new(); my %mods = map { $_->package() => 1 } map { $_->modules() } $cb->search( type => $type, allow => [ qr/$search/ ], [ verbose => 0 ] ); print qq{$_\n} for sort keys %mods;" ' . $type . " " . $search;
542 0 0         my $job = Win32::Job->new()
543             or die Win32::FormatMessage( Win32::GetLastError() );
544 0 0         my $pid = $job->spawn( $perl, $cmdline )
545             or die Win32::FormatMessage( Win32::GetLastError() );
546 0           warn $pid, "\n";
547 0     0     my $ok = $job->watch( sub { 0 }, 60 );
  0            
548 0           my $hashref = $job->status();
549 0           return $hashref->{$pid}->{exitcode};
550             }
551              
552             sub _package_search {
553 0     0     my $perl = shift;
554 0           my $type = shift;
555 0           my $search = shift;
556 0           my $cmdline = $perl . ' -MCPANPLUS::YACSmoke -e "my $type = shift; my $search = shift; my $cb = CPANPLUS::Backend->new(); my %mods = map { $_->package() => 1 } $cb->search( type => $type, allow => [ qr/$search/ ], [ verbose => 0 ] ); print qq{$_\n} for sort keys %mods;" ' . $type . " " . $search;
557 0 0         my $job = Win32::Job->new()
558             or die Win32::FormatMessage( Win32::GetLastError() );
559 0 0         my $pid = $job->spawn( $perl, $cmdline )
560             or die Win32::FormatMessage( Win32::GetLastError() );
561 0           warn $pid, "\n";
562 0     0     my $ok = $job->watch( sub { 0 }, 60 );
  0            
563 0           my $hashref = $job->status();
564 0           return $hashref->{$pid}->{exitcode};
565             }
566              
567             sub _kill_family {
568 0     0     my ($signal, @pids) = @_;
569 0           my $pt = Proc::ProcessTable->new;
570 0           my (@procs) = @{$pt->table};
  0            
571 0           my (@kids) = _get_pids( \@procs, @pids );
572 0           @pids = (@pids, @kids);
573 0           kill $signal, reverse @pids;
574             }
575              
576             sub _get_pids {
577 0     0     my($procs, @kids) = @_;
578 0           my @pids;
579 0           foreach my $kid (@kids) {
580 0           foreach my $proc (@$procs) {
581 0 0         if ($proc->ppid == $kid) {
582 0           my $pid = $proc->pid;
583 0           push @pids, $pid, _get_pids( $procs, $pid );
584             }
585             }
586             }
587 0           @pids;
588             }
589              
590             1;
591              
592             __END__