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