File Coverage

blib/lib/POE/Component/CPAN/YACSmoke.pm
Criterion Covered Total %
statement 183 358 51.1
branch 67 168 39.8
condition 15 80 18.7
subroutine 19 43 44.1
pod 10 10 100.0
total 294 659 44.6


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