This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Update Module-Load-Conditional to CPAN version 0.54
[perl5.git] / cpan / Module-Load-Conditional / lib / Module / Load / Conditional.pm
CommitLineData
0dc418cb
JB
1package Module::Load::Conditional;
2
3use strict;
4
5use Module::Load;
3fa779ab
JB
6use Params::Check qw[check];
7use Locale::Maketext::Simple Style => 'gettext';
0dc418cb
JB
8
9use Carp ();
10use File::Spec ();
11use FileHandle ();
aacdad3c 12use version;
0dc418cb 13
201db1c7
SH
14use Module::Metadata ();
15
3fa779ab
JB
16use constant ON_VMS => $^O eq 'VMS';
17
0dc418cb 18BEGIN {
1823d11b 19 use vars qw[ $VERSION @ISA $VERBOSE $CACHE @EXPORT_OK $DEPRECATED
0dc418cb
JB
20 $FIND_VERSION $ERROR $CHECK_INC_HASH];
21 use Exporter;
22 @ISA = qw[Exporter];
26968795 23 $VERSION = '0.54';
0dc418cb 24 $VERBOSE = 0;
1823d11b 25 $DEPRECATED = 0;
0dc418cb
JB
26 $FIND_VERSION = 1;
27 $CHECK_INC_HASH = 0;
28 @EXPORT_OK = qw[check_install can_load requires];
29}
30
31=pod
32
33=head1 NAME
34
35Module::Load::Conditional - Looking up module information / loading at runtime
36
37=head1 SYNOPSIS
38
39 use Module::Load::Conditional qw[can_load check_install requires];
40
41
42 my $use_list = {
43 CPANPLUS => 0.05,
44 LWP => 5.60,
45 'Test::More' => undef,
46 };
47
48 print can_load( modules => $use_list )
49 ? 'all modules loaded successfully'
50 : 'failed to load required modules';
51
52
53 my $rv = check_install( module => 'LWP', version => 5.60 )
54 or print 'LWP is not installed!';
55
56 print 'LWP up to date' if $rv->{uptodate};
57 print "LWP version is $rv->{version}\n";
58 print "LWP is installed as file $rv->{file}\n";
59
60
61 print "LWP requires the following modules to be installed:\n";
62 print join "\n", requires('LWP');
63
64 ### allow M::L::C to peek in your %INC rather than just
65 ### scanning @INC
66 $Module::Load::Conditional::CHECK_INC_HASH = 1;
67
68 ### reset the 'can_load' cache
69 undef $Module::Load::Conditional::CACHE;
70
71 ### don't have Module::Load::Conditional issue warnings --
72 ### default is '1'
73 $Module::Load::Conditional::VERBOSE = 0;
74
75 ### The last error that happened during a call to 'can_load'
76 my $err = $Module::Load::Conditional::ERROR;
77
78
79=head1 DESCRIPTION
80
81Module::Load::Conditional provides simple ways to query and possibly load any of
82the modules you have installed on your system during runtime.
83
84It is able to load multiple modules at once or none at all if one of
85them was not able to load. It also takes care of any error checking
86and so forth.
87
88=head1 Methods
89
f541799a 90=head2 $href = check_install( module => NAME [, version => VERSION, verbose => BOOL ] );
0dc418cb
JB
91
92C<check_install> allows you to verify if a certain module is installed
93or not. You may call it with the following arguments:
94
95=over 4
96
97=item module
98
99The name of the module you wish to verify -- this is a required key
100
101=item version
102
103The version this module needs to be -- this is optional
104
105=item verbose
106
107Whether or not to be verbose about what it is doing -- it will default
108to $Module::Load::Conditional::VERBOSE
109
110=back
111
112It will return undef if it was not able to find where the module was
113installed, or a hash reference with the following keys if it was able
114to find the file:
115
116=over 4
117
118=item file
119
120Full path to the file that contains the module
121
9b31c40c
SH
122=item dir
123
124Directory, or more exact the C<@INC> entry, where the module was
125loaded from.
126
0dc418cb
JB
127=item version
128
129The version number of the installed module - this will be C<undef> if
130the module had no (or unparsable) version number, or if the variable
131C<$Module::Load::Conditional::FIND_VERSION> was set to true.
132(See the C<GLOBAL VARIABLES> section below for details)
133
134=item uptodate
135
136A boolean value indicating whether or not the module was found to be
137at least the version you specified. If you did not specify a version,
138uptodate will always be true if the module was found.
139If no parsable version was found in the module, uptodate will also be
140true, since C<check_install> had no way to verify clearly.
141
21501d15 142See also C<$Module::Load::Conditional::DEPRECATED>, which affects
1823d11b
CBW
143the outcome of this value.
144
0dc418cb
JB
145=back
146
147=cut
148
149### this checks if a certain module is installed already ###
150### if it returns true, the module in question is already installed
151### or we found the file, but couldn't open it, OR there was no version
152### to be found in the module
153### it will return 0 if the version in the module is LOWER then the one
154### we are looking for, or if we couldn't find the desired module to begin with
155### if the installed version is higher or equal to the one we want, it will return
156### a hashref with he module name and version in it.. so 'true' as well.
157sub check_install {
158 my %hash = @_;
159
160 my $tmpl = {
161 version => { default => '0.0' },
162 module => { required => 1 },
163 verbose => { default => $VERBOSE },
164 };
165
166 my $args;
167 unless( $args = check( $tmpl, \%hash, $VERBOSE ) ) {
168 warn loc( q[A problem occurred checking arguments] ) if $VERBOSE;
169 return;
170 }
171
172 my $file = File::Spec->catfile( split /::/, $args->{module} ) . '.pm';
21501d15
CBW
173 my $file_inc = File::Spec::Unix->catfile(
174 split /::/, $args->{module}
0dc418cb
JB
175 ) . '.pm';
176
177 ### where we store the return value ###
178 my $href = {
179 file => undef,
180 version => undef,
181 uptodate => undef,
182 };
21501d15 183
0dc418cb
JB
184 my $filename;
185
186 ### check the inc hash if we're allowed to
187 if( $CHECK_INC_HASH ) {
21501d15 188 $filename = $href->{'file'} =
0dc418cb
JB
189 $INC{ $file_inc } if defined $INC{ $file_inc };
190
191 ### find the version by inspecting the package
192 if( defined $filename && $FIND_VERSION ) {
193 no strict 'refs';
21501d15 194 $href->{version} = ${ "$args->{module}"."::VERSION" };
0dc418cb 195 }
21501d15 196 }
0dc418cb
JB
197
198 ### we didnt find the filename yet by looking in %INC,
199 ### so scan the dirs
200 unless( $filename ) {
201
202 DIR: for my $dir ( @INC ) {
21501d15 203
0dc418cb 204 my $fh;
21501d15 205
0dc418cb
JB
206 if ( ref $dir ) {
207 ### @INC hook -- we invoke it and get the filehandle back
208 ### this is actually documented behaviour as of 5.8 ;)
a4031803
CBW
209
210 my $existed_in_inc = $INC{$file_inc};
21501d15 211
0dc418cb
JB
212 if (UNIVERSAL::isa($dir, 'CODE')) {
213 ($fh) = $dir->($dir, $file);
21501d15 214
0dc418cb
JB
215 } elsif (UNIVERSAL::isa($dir, 'ARRAY')) {
216 ($fh) = $dir->[0]->($dir, $file, @{$dir}{1..$#{$dir}})
21501d15 217
0dc418cb 218 } elsif (UNIVERSAL::can($dir, 'INC')) {
1823d11b 219 ($fh) = $dir->INC($file);
0dc418cb 220 }
21501d15 221
0dc418cb
JB
222 if (!UNIVERSAL::isa($fh, 'GLOB')) {
223 warn loc(q[Cannot open file '%1': %2], $file, $!)
224 if $args->{verbose};
225 next;
226 }
21501d15 227
0dc418cb 228 $filename = $INC{$file_inc} || $file;
a4031803
CBW
229
230 delete $INC{$file_inc} if not $existed_in_inc;
21501d15 231
0dc418cb
JB
232 } else {
233 $filename = File::Spec->catfile($dir, $file);
234 next unless -e $filename;
21501d15 235
0dc418cb
JB
236 $fh = new FileHandle;
237 if (!$fh->open($filename)) {
238 warn loc(q[Cannot open file '%1': %2], $file, $!)
239 if $args->{verbose};
240 next;
241 }
242 }
21501d15 243
9b31c40c
SH
244 ### store the directory we found the file in
245 $href->{dir} = $dir;
21501d15 246
3fa779ab
JB
247 ### files need to be in unix format under vms,
248 ### or they might be loaded twice
249 $href->{file} = ON_VMS
250 ? VMS::Filespec::unixify( $filename )
251 : $filename;
21501d15 252
201db1c7
SH
253 ### if we don't need the version, we're done
254 last DIR unless $FIND_VERSION;
21501d15 255
201db1c7
SH
256 ### otherwise, the user wants us to find the version from files
257 my $mod_info = Module::Metadata->new_from_handle( $fh, $filename );
258 my $ver = $mod_info->version( $args->{module} );
21501d15 259
201db1c7
SH
260 if( defined $ver ) {
261 $href->{version} = $ver;
91e53322 262
201db1c7 263 last DIR;
0dc418cb
JB
264 }
265 }
266 }
21501d15 267
0dc418cb
JB
268 ### if we couldn't find the file, return undef ###
269 return unless defined $href->{file};
270
3fa779ab 271 ### only complain if we're expected to find a version higher than 0.0 anyway
0dc418cb
JB
272 if( $FIND_VERSION and not defined $href->{version} ) {
273 { ### don't warn about the 'not numeric' stuff ###
274 local $^W;
275
276 ### if we got here, we didn't find the version
277 warn loc(q[Could not check version on '%1'], $args->{module} )
278 if $args->{verbose} and $args->{version} > 0;
279 }
280 $href->{uptodate} = 1;
281
282 } else {
283 ### don't warn about the 'not numeric' stuff ###
284 local $^W;
21501d15 285
3fa779ab
JB
286 ### use qv(), as it will deal with developer release number
287 ### ie ones containing _ as well. This addresses bug report
288 ### #29348: Version compare logic doesn't handle alphas?
aacdad3c
SP
289 ###
290 ### Update from JPeacock: apparently qv() and version->new
291 ### are different things, and we *must* use version->new
292 ### here, or things like #30056 might start happening
6ba8cc37
CBW
293
294 ### We have to wrap this in an eval as version-0.82 raises
295 ### exceptions and not warnings now *sigh*
296
297 eval {
298
21501d15 299 $href->{uptodate} =
aacdad3c 300 version->new( $args->{version} ) <= version->new( $href->{version} )
21501d15 301 ? 1
aacdad3c 302 : 0;
6ba8cc37
CBW
303
304 };
0dc418cb
JB
305 }
306
201db1c7 307 if ( $DEPRECATED and "$]" >= 5.011 ) {
1823d11b
CBW
308 require Module::CoreList;
309 require Config;
310
21501d15 311 $href->{uptodate} = 0 if
1823d11b
CBW
312 exists $Module::CoreList::version{ 0+$] }{ $args->{module} } and
313 Module::CoreList::is_deprecated( $args->{module} ) and
314 $Config::Config{privlibexp} eq $href->{dir};
315 }
316
0dc418cb
JB
317 return $href;
318}
319
320=head2 $bool = can_load( modules => { NAME => VERSION [,NAME => VERSION] }, [verbose => BOOL, nocache => BOOL] )
321
322C<can_load> will take a list of modules, optionally with version
323numbers and determine if it is able to load them. If it can load *ALL*
324of them, it will. If one or more are unloadable, none will be loaded.
325
326This is particularly useful if you have More Than One Way (tm) to
327solve a problem in a program, and only wish to continue down a path
328if all modules could be loaded, and not load them if they couldn't.
329
330This function uses the C<load> function from Module::Load under the
331hood.
332
333C<can_load> takes the following arguments:
334
335=over 4
336
337=item modules
338
339This is a hashref of module/version pairs. The version indicates the
340minimum version to load. If no version is provided, any version is
341assumed to be good enough.
342
343=item verbose
344
345This controls whether warnings should be printed if a module failed
346to load.
347The default is to use the value of $Module::Load::Conditional::VERBOSE.
348
349=item nocache
350
351C<can_load> keeps its results in a cache, so it will not load the
352same module twice, nor will it attempt to load a module that has
353already failed to load before. By default, C<can_load> will check its
354cache, but you can override that by setting C<nocache> to true.
355
356=cut
357
358sub can_load {
359 my %hash = @_;
360
361 my $tmpl = {
362 modules => { default => {}, strict_type => 1 },
363 verbose => { default => $VERBOSE },
364 nocache => { default => 0 },
365 };
366
367 my $args;
368
369 unless( $args = check( $tmpl, \%hash, $VERBOSE ) ) {
370 $ERROR = loc(q[Problem validating arguments!]);
371 warn $ERROR if $VERBOSE;
372 return;
373 }
374
375 ### layout of $CACHE:
376 ### $CACHE = {
377 ### $ module => {
378 ### usable => BOOL,
379 ### version => \d,
380 ### file => /path/to/file,
381 ### },
382 ### };
383
384 $CACHE ||= {}; # in case it was undef'd
385
386 my $error;
387 BLOCK: {
388 my $href = $args->{modules};
389
390 my @load;
391 for my $mod ( keys %$href ) {
392
393 next if $CACHE->{$mod}->{usable} && !$args->{nocache};
394
395 ### else, check if the hash key is defined already,
396 ### meaning $mod => 0,
397 ### indicating UNSUCCESSFUL prior attempt of usage
3fa779ab
JB
398
399 ### use qv(), as it will deal with developer release number
400 ### ie ones containing _ as well. This addresses bug report
401 ### #29348: Version compare logic doesn't handle alphas?
aacdad3c
SP
402 ###
403 ### Update from JPeacock: apparently qv() and version->new
404 ### are different things, and we *must* use version->new
21501d15 405 ### here, or things like #30056 might start happening
0dc418cb
JB
406 if ( !$args->{nocache}
407 && defined $CACHE->{$mod}->{usable}
21501d15 408 && (version->new( $CACHE->{$mod}->{version}||0 )
aacdad3c 409 >= version->new( $href->{$mod} ) )
0dc418cb
JB
410 ) {
411 $error = loc( q[Already tried to use '%1', which was unsuccessful], $mod);
412 last BLOCK;
413 }
414
415 my $mod_data = check_install(
416 module => $mod,
417 version => $href->{$mod}
418 );
419
420 if( !$mod_data or !defined $mod_data->{file} ) {
421 $error = loc(q[Could not find or check module '%1'], $mod);
422 $CACHE->{$mod}->{usable} = 0;
423 last BLOCK;
424 }
425
426 map {
427 $CACHE->{$mod}->{$_} = $mod_data->{$_}
428 } qw[version file uptodate];
429
430 push @load, $mod;
431 }
432
433 for my $mod ( @load ) {
434
435 if ( $CACHE->{$mod}->{uptodate} ) {
436
437 eval { load $mod };
438
439 ### in case anything goes wrong, log the error, the fact
440 ### we tried to use this module and return 0;
441 if( $@ ) {
442 $error = $@;
443 $CACHE->{$mod}->{usable} = 0;
444 last BLOCK;
445 } else {
446 $CACHE->{$mod}->{usable} = 1;
447 }
448
449 ### module not found in @INC, store the result in
450 ### $CACHE and return 0
451 } else {
452
453 $error = loc(q[Module '%1' is not uptodate!], $mod);
454 $CACHE->{$mod}->{usable} = 0;
455 last BLOCK;
456 }
457 }
458
459 } # BLOCK
460
461 if( defined $error ) {
462 $ERROR = $error;
463 Carp::carp( loc(q|%1 [THIS MAY BE A PROBLEM!]|,$error) ) if $args->{verbose};
3fa779ab 464 return;
0dc418cb
JB
465 } else {
466 return 1;
467 }
468}
469
3fa779ab
JB
470=back
471
0dc418cb
JB
472=head2 @list = requires( MODULE );
473
474C<requires> can tell you what other modules a particular module
475requires. This is particularly useful when you're intending to write
476a module for public release and are listing its prerequisites.
477
478C<requires> takes but one argument: the name of a module.
479It will then first check if it can actually load this module, and
480return undef if it can't.
481Otherwise, it will return a list of modules and pragmas that would
482have been loaded on the module's behalf.
483
484Note: The list C<require> returns has originated from your current
485perl and your current install.
486
487=cut
488
489sub requires {
490 my $who = shift;
491
492 unless( check_install( module => $who ) ) {
493 warn loc(q[You do not have module '%1' installed], $who) if $VERBOSE;
494 return undef;
495 }
496
497 my $lib = join " ", map { qq["-I$_"] } @INC;
21501d15 498 my $cmd = qq["$^X" $lib -M$who -e"print(join(qq[\\n],keys(%INC)))"];
0dc418cb
JB
499
500 return sort
501 grep { !/^$who$/ }
502 map { chomp; s|/|::|g; $_ }
503 grep { s|\.pm$||i; }
504 `$cmd`;
505}
506
5071;
508
509__END__
510
511=head1 Global Variables
512
513The behaviour of Module::Load::Conditional can be altered by changing the
514following global variables:
515
516=head2 $Module::Load::Conditional::VERBOSE
517
518This controls whether Module::Load::Conditional will issue warnings and
519explanations as to why certain things may have failed. If you set it
520to 0, Module::Load::Conditional will not output any warnings.
521The default is 0;
522
523=head2 $Module::Load::Conditional::FIND_VERSION
524
525This controls whether Module::Load::Conditional will try to parse
21501d15 526(and eval) the version from the module you're trying to load.
0dc418cb
JB
527
528If you don't wish to do this, set this variable to C<false>. Understand
529then that version comparisons are not possible, and Module::Load::Conditional
530can not tell you what module version you have installed.
21501d15 531This may be desirable from a security or performance point of view.
0dc418cb
JB
532Note that C<$FIND_VERSION> code runs safely under C<taint mode>.
533
534The default is 1;
535
536=head2 $Module::Load::Conditional::CHECK_INC_HASH
537
538This controls whether C<Module::Load::Conditional> checks your
539C<%INC> hash to see if a module is available. By default, only
540C<@INC> is scanned to see if a module is physically on your
8c2265fd 541filesystem, or available via an C<@INC-hook>. Setting this variable
0dc418cb
JB
542to C<true> will trust any entries in C<%INC> and return them for
543you.
544
545The default is 0;
546
547=head2 $Module::Load::Conditional::CACHE
548
549This holds the cache of the C<can_load> function. If you explicitly
550want to remove the current cache, you can set this variable to
551C<undef>
552
553=head2 $Module::Load::Conditional::ERROR
554
555This holds a string of the last error that happened during a call to
556C<can_load>. It is useful to inspect this when C<can_load> returns
557C<undef>.
558
1823d11b
CBW
559=head2 $Module::Load::Conditional::DEPRECATED
560
21501d15 561This controls whether C<Module::Load::Conditional> checks if
1823d11b 562a dual-life core module has been deprecated. If this is set to
21501d15 563true C<check_install> will return false to C<uptodate>, if
1823d11b
CBW
564a dual-life module is found to be loaded from C<$Config{privlibexp}>
565
566The default is 0;
567
0dc418cb
JB
568=head1 See Also
569
570C<Module::Load>
571
3fa779ab
JB
572=head1 BUG REPORTS
573
574Please report bugs or other issues to E<lt>bug-module-load-conditional@rt.cpan.orgE<gt>.
575
0dc418cb
JB
576=head1 AUTHOR
577
3fa779ab 578This module by Jos Boumans E<lt>kane@cpan.orgE<gt>.
0dc418cb
JB
579
580=head1 COPYRIGHT
581
21501d15 582This library is free software; you may redistribute and/or modify it
3fa779ab 583under the same terms as Perl itself.
0dc418cb 584
3fa779ab 585=cut