How do I use a Perl package known only in runtime?

2019-01-18 11:01发布

问题:

I have a Perl program, that needs to use packages (that I also write). Some of those packages are only chosen in Runtime (based on some environment variable). I don't want to put in my code a "use" line for all of those packages, of course, but only one "use" line, based on this variable, something like:

use $ENV{a};

Unfortunately, this doesn't work, of course. Any ideas on how to do this?

Thanks in advance, Oren

回答1:

eval "require $ENV{a}";

"use" doesn't work well here because it only imports in the context of the eval.

As @Manni said, actually, it's better to use require. Quoting from man perlfunc:

If EXPR is a bareword, the require assumes a ".pm" extension and 
replaces "::" with "/" in the filename for you, to make it easy to 
load standard modules.  This form of  loading of modules does not 
risk altering your namespace.

In other words, if you try this:

        require Foo::Bar;    # a splendid bareword

The require function will actually look for the "Foo/Bar.pm" file 
in the directories specified in the @INC array.

But if you try this:

        $class = 'Foo::Bar';
        require $class;      # $class is not a bareword
    #or
        require "Foo::Bar";  # not a bareword because of the ""

The require function will look for the "Foo::Bar" file in the @INC 
array and will complain about not finding "Foo::Bar" there.  In this 
case you can do:

        eval "require $class";


回答2:

"use" Statements are run at compilation time, not at run time. You will need to require your modules instead:

my $module = "Foo::Bar";
eval "require $module";


回答3:

I would use UNIVERSAL::require. It has both require and use methods to use a package. The use method will also call import for the package.

use UNIVERSAL::require;
$ENV{a}->use or die 'Could not import package:  ' . $@;


回答4:

You probably want to use require instead of use if you don't want it to happen at compile time, and then manually import any symbols you might need. See this link to the Perl Cookbook (from Google Books) for a good discussion of methods you can use to achieve what you want.



回答5:

I think that a module loaded in runtime can be a Plugin. I have this kind of problem, having specific modules to some cases that are loaded in run time as plugins with Module::Pluggable.

Maybe you need to change the logic of your modules, but it works and scale very well (my app started with four modules and now have twenty and it's growing).



回答6:

How about using the core module Module::Load

With your example:

use Module::Load;
load $ENV{a};

"Module::Load - runtime require of both modules and files"

"load eliminates the need to know whether you are trying to require either a file or a module."

If it fails it will die with something of the like "Can't locate xxx in @INC (@INC contains: ...".



回答7:

Many years later, eval "use $mod_name"; seems to work just fine (as of at least 5.8.8).

The advantage over eval "require $mod_name"; is that the loaded module's default exports are automatically imported; in other words:

eval "use $mod_name";

is the shorter equivalent of

eval "require $mod_name"; $mod_name->import();

Here's a test command, which passes the name of the module via env. variable mod_name, loads and imports the module, then uses an imported function (assumes a POSIX-like shell):

 $ mod_name='Data::Dumper' perl -e 'eval "use $ENV{mod_name}"; print Dumper("hi!")'
 $VAR1 = 'hi!';

I may be missing subtleties; if so, please let me know.