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