Version change to ExtUtils::MM_Unix missed in change #30380.
[p5sagit/p5-mst-13.2.git] / lib / CPANPLUS / Selfupdate.pm
CommitLineData
6aaee015 1package CPANPLUS::Selfupdate;
2
3use strict;
4use Params::Check qw[check];
5use IPC::Cmd qw[can_run];
6use CPANPLUS::Error qw[error msg];
7use Locale::Maketext::Simple Class => 'CPANPLUS', Style => 'gettext';
8
9use CPANPLUS::Internals::Constants;
10
11$Params::Check::VERBOSE = 1;
12
13=head1 NAME
14
15CPANPLUS::Selfupdate
16
17=head1 SYNOPSIS
18
19 $su = $cb->selfupdate_object;
20
21 @feats = $su->list_features;
22 @feats = $su->list_enabled_features;
23
24 @mods = map { $su->modules_for_feature( $_ ) } @feats;
25 @mods = $su->list_core_dependencies;
26 @mods = $su->list_core_modules;
27
28 for ( @mods ) {
29 print $_->name " should be version " . $_->version_required;
30 print "Installed version is not uptodate!"
31 unless $_->is_installed_version_sufficient;
32 }
33
34 $ok = $su->selfupdate( update => 'all', latest => 0 );
35
36=cut
37
38### a config has describing our deps etc
39{
40
41 my $Modules = {
42 dependencies => {
43 'File::Fetch' => '0.08', # win32 ftp support
44 'File::Spec' => '0.82',
45 'IPC::Cmd' => '0.36', # 5.6.2 compat: 2-arg open
46 'Locale::Maketext::Simple' => '0.01',
47 'Log::Message' => '0.01',
48 'Module::Load' => '0.10',
49 'Module::Load::Conditional' => '0.16', # Better parsing: #23995
50 'version' => '0.70', # needed for M::L::C
51 # addresses #24630 and
52 # #24675
53 'Params::Check' => '0.22',
54 'Package::Constants' => '0.01',
55 'Term::UI' => '0.05',
56 'Test::Harness' => '2.62', # due to bug #19505
57 # only 2.58 and 2.60 are bad
58 'Test::More' => '0.47', # to run our tests
59 'Archive::Extract' => '0.16', # ./Dir bug fix
60 'Archive::Tar' => '1.23',
61 'IO::Zlib' => '1.04', # needed for Archive::Tar
62 'Object::Accessor' => '0.32', # overloaded stringification
63 'Module::CoreList' => '2.09',
64 'Module::Pluggable' => '2.4',
65 'Module::Loaded' => '0.01',
66 },
67
68 features => {
69 # config_key_name => [
70 # sub { } to list module key/value pairs
71 # sub { } to check if feature is enabled
72 # ]
73 prefer_makefile => [
74 sub {
75 my $cb = shift;
76 $cb->configure_object->get_conf('prefer_makefile')
77 ? { }
78 : { 'CPANPLUS::Dist::Build' => '0.04' };
79 },
80 sub { return 1 }, # always enabled
81 ],
82 cpantest => [
83 {
84 LWP => '0.0',
85 'LWP::UserAgent' => '0.0',
86 'HTTP::Request' => '0.0',
87 URI => '0.0',
88 YAML => '0.0',
89 'Test::Reporter' => 1.27,
90 },
91 sub {
92 my $cb = shift;
93 return $cb->configure_object->get_conf('cpantest');
94 },
95 ],
96 dist_type => [
97 sub {
98 my $cb = shift;
99 my $dist = $cb->configure_object->get_conf('dist_type');
100 return { $dist => '0.0' } if $dist;
101 return;
102 },
103 sub {
104 my $cb = shift;
105 return $cb->configure_object->get_conf('dist_type');
106 },
107 ],
108
109 md5 => [
110 {
111 'Digest::MD5' => '0.0',
112 },
113 sub {
114 my $cb = shift;
115 return $cb->configure_object->get_conf('md5');
116 },
117 ],
118 shell => [
119 sub {
120 my $cb = shift;
121 my $dist = $cb->configure_object->get_conf('shell');
122 return { $dist => '0.0' } if $dist;
123 return;
124 },
125 sub { return 1 },
126 ],
127 signature => [
128 sub {
129 my $cb = shift;
130 return if can_run('gpg') and
131 $cb->configure_object->get_conf('prefer_bin');
132 return { 'Crypt::OpenPGP' => '0.0' };
133 },
134 sub {
135 my $cb = shift;
136 return $cb->configure_object->get_conf('signature');
137 },
138 ],
139 storable => [
140 { 'Storable' => '0.0' },
141 sub {
142 my $cb = shift;
143 return $cb->configure_object->get_conf('storable');
144 },
145 ],
146 },
147 core => {
148 'CPANPLUS' => '0.0',
149 },
150 };
151
152 sub _get_config { return $Modules }
153}
154
155=head1 METHODS
156
157=head2 $self = CPANPLUS::Selfupdate->new( $backend_object );
158
159Sets up a new selfupdate object. Called automatically when
160a new backend object is created.
161
162=cut
163
164sub new {
165 my $class = shift;
166 my $cb = shift or return;
167 return bless sub { $cb }, $class;
168}
169
170
171
172=head2 $bool = $self->selfupdate( update => "core|dependencies|enabled_features|features|all", latest => BOOL )
173
174Selfupdate CPANPLUS. You can update either the core (CPANPLUS itself),
175the core dependencies, all features you have currently turned on, or
176all features available, or everything.
177
178The C<latest> option determines whether it should update to the latest
179version on CPAN, or if the minimal required version for CPANPLUS is
180good enough.
181
182Returns true on success, false on error.
183
184=cut
185
186sub selfupdate {
187 my $self = shift;
188 my $cb = $self->();
189 my $conf = $cb->configure_object;
190 my %hash = @_;
191
192 ### cache to find the relevant modules
193 my $cache = {
194 core => sub { $self->list_core_modules },
195 dependencies => sub { $self->list_core_dependencies },
196 enabled_features => sub { map { $self->modules_for_feature( $_ ) }
197 $self->list_enabled_features
198 },
199 features => sub { map { $self->modules_for_feature( $_ ) }
200 $self->list_features
201 },
202 ### make sure to do 'core' first, in case
203 ### we are out of date ourselves
204 all => [ qw|core dependencies enabled_features| ],
205 };
206
207 my($type, $latest, $force);
208 my $tmpl = {
209 update => { required => 1, store => \$type,
210 allow => [ keys %$cache ], },
211 latest => { default => 0, store => \$latest, allow => BOOLEANS },
212 force => { default => $conf->get_conf('force'), store => \$force },
213 };
214
215 check( $tmpl, \%hash ) or return;
216
217 my $ref = $cache->{$type};
218 my @mods = UNIVERSAL::isa( $ref, 'ARRAY' )
219 ? map { $cache->{$_}->() } @$ref
220 : $ref->();
221
222 ### do we need the latest versions?
223 @mods = $latest
224 ? @mods
225 : grep { $_->is_installed_version_sufficient } @mods;
226
227 my $flag;
228 for my $mod ( @mods ) {
229 unless( $mod->install( force => $force ) ) {
230 $flag++;
231 error(loc("Failed to update module '%1'", $mod->name));
232 }
233 }
234
235 return if $flag;
236 return 1;
237}
238
239=head2 @features = $self->list_features
240
241Returns a list of features that are supported by CPANPLUS.
242
243=cut
244
245sub list_features {
246 my $self = shift;
247 return keys %{ $self->_get_config->{'features'} };
248}
249
250=head2 @features = $self->list_enabled_features
251
252Returns a list of features that are enabled in your current
253CPANPLUS installation.
254
255=cut
256
257sub list_enabled_features {
258 my $self = shift;
259 my $cb = $self->();
260
261 my @enabled;
262 for my $feat ( $self->list_features ) {
263 my $ref = $self->_get_config->{'features'}->{$feat}->[1];
264 push @enabled, $feat if $ref->($cb);
265 }
266
267 return @enabled;
268}
269
270=head2 @mods = $self->modules_for_feature( FEATURE [,AS_HASH] )
271
272Returns a list of C<CPANPLUS::Selfupdate::Module> objects which
273represent the modules required to support this feature.
274
275For a list of features, call the C<list_features> method.
276
277If the C<AS_HASH> argument is provided, no module objects are
278returned, but a hashref where the keys are names of the modules,
279and values are their minimum versions.
280
281=cut
282
283sub modules_for_feature {
284 my $self = shift;
285 my $feature = shift or return;
286 my $as_hash = shift || 0;
287 my $cb = $self->();
288
289 unless( exists $self->_get_config->{'features'}->{$feature} ) {
290 error(loc("Unknown feature '%1'", $feature));
291 return;
292 }
293
294 my $ref = $self->_get_config->{'features'}->{$feature}->[0];
295
296 ### it's either a list of modules/versions or a subroutine that
297 ### returns a list of modules/versions
298 my $href = UNIVERSAL::isa( $ref, 'HASH' ) ? $ref : $ref->( $cb );
299
300 return unless $href; # nothing needed for the feature?
301
302 return $href if $as_hash;
303 return $self->_hashref_to_module( $href );
304}
305
306
307=head2 @mods = $self->list_core_dependencies( [AS_HASH] )
308
309Returns a list of C<CPANPLUS::Selfupdate::Module> objects which
310represent the modules that comprise the core dependencies of CPANPLUS.
311
312If the C<AS_HASH> argument is provided, no module objects are
313returned, but a hashref where the keys are names of the modules,
314and values are their minimum versions.
315
316=cut
317
318sub list_core_dependencies {
319 my $self = shift;
320 my $as_hash = shift || 0;
321 my $cb = $self->();
322 my $href = $self->_get_config->{'dependencies'};
323
324 return $href if $as_hash;
325 return $self->_hashref_to_module( $href );
326}
327
328=head2 @mods = $self->list_core_modules( [AS_HASH] )
329
330Returns a list of C<CPANPLUS::Selfupdate::Module> objects which
331represent the modules that comprise the core of CPANPLUS.
332
333If the C<AS_HASH> argument is provided, no module objects are
334returned, but a hashref where the keys are names of the modules,
335and values are their minimum versions.
336
337=cut
338
339sub list_core_modules {
340 my $self = shift;
341 my $as_hash = shift || 0;
342 my $cb = $self->();
343 my $href = $self->_get_config->{'core'};
344
345 return $href if $as_hash;
346 return $self->_hashref_to_module( $href );
347}
348
349sub _hashref_to_module {
350 my $self = shift;
351 my $cb = $self->();
352 my $href = shift or return;
353
354 return map {
355 CPANPLUS::Selfupdate::Module->new(
356 $cb->module_tree($_) => $href->{$_}
357 )
358 } keys %$href;
359}
360
361
362=head1 CPANPLUS::Selfupdate::Module
363
364C<CPANPLUS::Selfupdate::Module> extends C<CPANPLUS::Module> objects
365by providing accessors to aid in selfupdating CPANPLUS.
366
367These objects are returned by all methods of C<CPANPLUS::Selfupdate>
368that return module objects.
369
370=cut
371
372{ package CPANPLUS::Selfupdate::Module;
373 use base 'CPANPLUS::Module';
374
375 ### stores module name -> cpanplus required version
376 ### XXX only can deal with 1 pair!
377 my %Cache = ();
378 my $Acc = 'version_required';
379
380 sub new {
381 my $class = shift;
382 my $mod = shift or return;
383 my $ver = shift; return unless defined $ver;
384
385 my $obj = $mod->clone; # clone the module object
386 bless $obj, $class; # rebless it to our class
387
388 $obj->$Acc( $ver );
389
390 return $obj;
391 }
392
393=head2 $version = $mod->version_required
394
395Returns the version of this module required for CPANPLUS.
396
397=cut
398
399 sub version_required {
400 my $self = shift;
401 $Cache{ $self->name } = shift() if @_;
402 return $Cache{ $self->name };
403 }
404
405=head2 $bool = $mod->is_installed_version_sufficient
406
407Returns true if the installed version of this module is sufficient
408for CPANPLUS, or false if it is not.
409
410=cut
411
412
413 sub is_installed_version_sufficient {
414 my $self = shift;
415 return $self->is_uptodate( version => $self->$Acc );
416 }
417
418}
419
4201;
421
422=pod
423
424=head1 BUG REPORTS
425
426Please report bugs or other issues to E<lt>bug-cpanplus@rt.cpan.org<gt>.
427
428=head1 AUTHOR
429
430This module by Jos Boumans E<lt>kane@cpan.orgE<gt>.
431
432=head1 COPYRIGHT
433
434The CPAN++ interface (of which this module is a part of) is copyright (c)
4352001 - 2007, Jos Boumans E<lt>kane@cpan.orgE<gt>. All rights reserved.
436
437This library is free software; you may redistribute and/or modify it
438under the same terms as Perl itself.
439
440=cut
441
442# Local variables:
443# c-indentation-style: bsd
444# c-basic-offset: 4
445# indent-tabs-mode: nil
446# End:
447# vim: expandtab shiftwidth=4: