2 # This file is part of App::CPAN2Pkg.
3 # Copyright (c) 2009 Jerome Quelin, all rights reserved.
5 # This program is free software; you can redistribute it and/or modify
6 # it under the same terms as Perl itself.
10 package App
::CPAN2Pkg
::Module
;
17 constructor
=> '_new',
21 _prereqs
=> '_prereqs',
25 use POE
::Filter
::Line
;
28 my $rpm_locked = ''; # only one rpm transaction at a time
32 # $ apt-file find Audio/MPD.pm
33 # libaudio-mpd-perl: /usr/share/perl5/Audio/MPD.pm
35 # - find dist hosting module
36 # - computing dependencies
37 # - installing dependencies
38 # - check cooker availability
41 # - check local availability
44 # - wait for kenobi build
54 my ($class, $module) = @_;
57 my $obj = App
::CPAN2Pkg
::Module
->_new(
63 # spawning the session
64 my $session = POE
::Session
->create(
67 cpan2dist
=> \
&cpan2dist
,
68 find_prereqs
=> \
&find_prereqs
,
69 install_from_dist
=> \
&install_from_dist
,
70 is_in_dist
=> \
&is_in_dist
,
71 is_installed
=> \
&is_installed
,
73 _cpan2dist
=> \
&_cpan2dist
,
74 _find_prereqs
=> \
&_find_prereqs
,
75 _install_from_dist
=> \
&_install_from_dist
,
76 _is_in_dist
=> \
&_is_in_dist
,
81 #_stop => sub { warn "stop " . $_[HEAP]->name . "\n"; },
95 my ($k, $self) = @_[KERNEL
, HEAP
];
98 my $name = $self->name;
99 my $cmd = "cpan2dist --force --format=CPANPLUS::Dist::Mdv $name";
100 $self->_log_new_step('Building package', "Running command: $cmd" );
105 my $wheel = POE
::Wheel
::Run
->new(
107 CloseEvent
=> '_cpan2dist',
108 StdoutEvent
=> '_stdout',
109 StderrEvent
=> '_stderr',
110 StdoutFilter
=> POE
::Filter
::Line
->new,
111 StderrFilter
=> POE
::Filter
::Line
->new,
114 # need to store the wheel, otherwise the process goes woo!
115 $self->_wheel($wheel);
119 my ($k, $self) = @_[KERNEL
, HEAP
];
122 my $module = $self->name;
123 my $cmd = "cpanp /prereqs show $module";
124 $self->_log_new_step('Finding module prereqs', "Running command: $cmd" );
129 my $wheel = POE
::Wheel
::Run
->new(
131 CloseEvent
=> '_find_prereqs',
132 StdoutEvent
=> '_stdout',
133 StderrEvent
=> '_stderr',
134 StdoutFilter
=> POE
::Filter
::Line
->new,
135 StderrFilter
=> POE
::Filter
::Line
->new,
138 # need to store the wheel, otherwise the process goes woo!
139 $self->_wheel($wheel);
142 sub install_from_dist
{
143 my ($k, $self) = @_[KERNEL
, HEAP
];
144 my $name = $self->name;
146 # check whether there's another rpm transaction
148 $self->_log_prefixed_lines("waiting for rpm lock... (owned by $rpm_locked)");
149 $k->delay( install_from_dist
=> 1 );
155 my $cmd = "sudo urpmi --auto 'perl($name)'";
156 $self->_log_new_step('Installing from upstream', "Running command: $cmd" );
161 my $wheel = POE
::Wheel
::Run
->new(
163 StdoutEvent
=> '_stdout',
164 StderrEvent
=> '_stderr',
165 Conduit
=> 'pty-pipe', # urpmi wants a pty
166 StdoutFilter
=> POE
::Filter
::Line
->new,
167 StderrFilter
=> POE
::Filter
::Line
->new,
169 $k->sig( CHLD
=> '_install_from_dist' );
171 # need to store the wheel, otherwise the process goes woo!
172 $self->_wheel($wheel);
176 my ($k, $self) = @_[KERNEL
, HEAP
];
179 my $name = $self->name;
180 my $cmd = "urpmq --whatprovides 'perl($name)'";
181 $self->_log_new_step('Checking if packaged upstream', "Running command: $cmd" );
186 my $wheel = POE
::Wheel
::Run
->new(
188 #CloseEvent => '_is_in_dist', # FIXME: cf rt#42757
189 StdoutEvent
=> '_stdout',
190 StderrEvent
=> '_stderr',
191 Conduit
=> 'pty-pipe', # urpmq wants a pty
192 StdoutFilter
=> POE
::Filter
::Line
->new,
193 StderrFilter
=> POE
::Filter
::Line
->new,
195 $k->sig( CHLD
=> '_is_in_dist' );
197 # need to store the wheel, otherwise the process goes woo!
198 $self->_wheel($wheel);
203 my ($k, $self) = @_[KERNEL
, HEAP
];
205 my $name = $self->name;
206 my $cmd = qq{ require $name };
207 $self->_log_new_step(
208 'Checking if module is installed',
209 "Evaluating command: $cmd"
213 my $what = $@
|| "$name loaded successfully\n";
214 $k->post('ui', 'append', $self, $what);
216 my $is_installed = $@
eq '';
217 my $status = $is_installed ?
'installed' : 'not installed';
218 $self->_log_result("$name is $status locally.");
219 $k->post('app', 'local_status', $self, $is_installed);
225 my ($k, $self, $id) = @_[KERNEL
, HEAP
, ARG0
];
226 my $name = $self->name;
229 my $wheel = $self->_wheel;
230 $self->_wheel(undef);
232 my $output = $self->_output;
234 $rpm = $1 if $output =~ /rpm created successfully: (.*\.rpm)/;
235 $srpm = $1 if $output =~ /srpm available: (.*\.src.rpm)/;
237 if ( $rpm && $srpm ) {
239 "$name has been successfully built",
240 "srpm created: $srpm",
243 $k->post('app', 'cpan2dist', $self, 1);
245 $self->_log_result("error while building $name");
246 $k->post('app', 'cpan2dist', $self, 0);
251 my ($k, $self, $id) = @_[KERNEL
, HEAP
, ARG0
];
254 my $wheel = $self->_wheel;
255 $self->_wheel(undef);
260 split /\n/, $self->_output;
261 shift @lines; # remove the title line
263 map { (split /\s+/, $_)[0] }
268 ?
map { "prereq found: $_" } @prereqs
269 : 'No prereqs found.';
270 $self->_log_result(@logs);
271 $k->post('app', 'prereqs', $self, @prereqs);
274 sub _install_from_dist
{
275 my($k, $self, $pid, $rv) = @_[KERNEL
, HEAP
, ARG1
, ARG2
];
277 # since it's a sigchld handler, it also gets called for other
278 # spawned processes. therefore, screen out processes that are
279 # not related to this object.
280 return unless defined $self->_wheel;
281 return unless $self->_wheel->PID == $pid;
284 $self->_wheel(undef);
290 my $name = $self->name;
291 my $exval = $rv >> 8;
292 my $status = $exval ?
'not been' : 'been';
293 $self->_log_result( "$name has $status installed from upstream." );
294 $k->post('app', 'upstream_install', $self, !$exval);
298 my($k, $self, $pid, $rv) = @_[KERNEL
, HEAP
, ARG1
, ARG2
];
300 # since it's a sigchld handler, it also gets called for other
301 # spawned processes. therefore, screen out processes that are
302 # not related to this object.
303 return unless defined $self->_wheel;
304 return unless $self->_wheel->PID == $pid;
307 # FIXME: should be done in CloseEvent
308 $self->_wheel(undef);
310 # check if we got a hit
311 # urpmq returns 0 if found, 1 otherwise.
312 my $name = $self->name;
313 my $exval = $rv >> 8;
315 my $status = $exval ?
'not' : 'already';
316 $self->_log_result( "$name is $status packaged upstream." );
317 $k->post('app', 'upstream_status', $self, !$exval);
321 my ($k, $self, $line) = @_[KERNEL
, HEAP
, ARG0
];
322 $k->post('ui', 'append', $self, "stderr: $line\n");
326 my ($k, $self, $line) = @_[KERNEL
, HEAP
, ARG0
];
328 $self->_output( $self->_output . $line );
329 $k->post('ui', 'append', $self, "stdout: $line");
333 # -- poe inline states
336 my ($k, $self) = @_[KERNEL
, HEAP
];
338 $k->alias_set($self);
339 $k->post('ui', 'module_spawned', $self);
340 $k->post('app', 'module_spawned', $self);
349 sub _log_empty_line
{
350 my ($self, $nb) = @_;
351 $nb //= 1; #/ FIXME padre syntaxic color glitch
352 POE
::Kernel
->post('ui', 'append', $self, "\n" x
$nb);
355 sub _log_prefixed_lines
{
356 my ($self, @lines) = @_;
359 POE
::Kernel
->post('ui', 'append', $self, $_)
360 for map { "$prefix $_\n" } @lines;
364 my ($self, $step, $comment) = @_;
366 $self->_log_prefixed_lines('-' x
10, $step, '', $comment, '');
367 $self->_log_empty_line;
371 my ($self, @lines) = @_;
373 $self->_log_empty_line;
374 $self->_log_prefixed_lines( '', @lines, '', '' );
384 App::CPAN2Pkg::Module - poe session to drive a module packaging
390 C<App::CPAN2Pkg::Module> implements a POE session driving the whole
391 packaging process of a given module.
393 It is spawned by C<App::CPAN2Pkg> and implements the logic related to
394 the module availability in the distribution.
398 =head1 PUBLIC PACKAGE METHODS
400 =head2 my $id = App::CPAN2Pkg::Module->spawn( $module )
402 This method will create a POE session responsible for packaging &
403 installing the wanted C<$module>.
405 It will return the POE id of the session newly created.
409 =head1 PUBLIC EVENTS ACCEPTED
413 Build a native package for this module, using C<cpan2dist> with the C<--force> flag.
416 =head2 find_prereqs()
418 Start looking for any other module needed by current module.
421 =head2 install_from_dist()
423 Try to install module from upstream distribution.
428 Check whether the package is provided by an existing upstream package.
431 =head2 is_installed()
433 Check whether the package is installed locally.
438 This package is also a class, used B<internally> to store private data
439 needed for the packaging stuff. The following accessors are therefore
440 available, but should not be used directly:
444 =item name() - the module name
452 For all related information (bug reporting, source code repository,
453 etc.), refer to C<App::CPAN2Pkg>'s pod, section C<SEE ALSO>.
459 Jerome Quelin, C<< <jquelin@cpan.org> >>
463 =head1 COPYRIGHT & LICENSE
465 Copyright (c) 2009 Jerome Quelin, all rights reserved.
467 This program is free software; you can redistribute it and/or modify
468 it under the same terms as Perl itself.