Question

How do I load a module with a path not in the @INC in Perl?

I've read the answers on how to add a path to the @INC, but the issue is that @INC has to be changed at the beginning of the code. After it's compiled, all the modules I've come across look to @INC to find the location of the module. My issue is that we're trying to separate out all these environment specific things to a config file. If it's in a config file, the path needs to be read before it can be pushed to @INC, but then the code has already been compiled and it seems @INC can't be modified.

  1. Is there a way? Is there a library that lets me load a module and pass it a custom path?
  2. Is this a terrible bad thing to do? Why?
Was it helpful?

Solution

Perl has an incremental compilation model which means that code can be executed before other code is even parsed. For this, we can use phase blocks (aka. phasers):

BEGIN {
    print "This is executed as soon as the block has been parsed\n";
}

Such a phase block could also be used to load a configuration file.

For example, use statements are effectively syntactic sugar for a BEGIN block.

use Foo::Bar qw/baz qux/;

is equivalent to

BEGIN {
    require Foo::Bar; # or: require "Foo/Bar.pm";
    Foo::Bar->import(qw/baz qux/);
}

We can also load modules at runtime, although that's only sensible for object-oriented modules.

So we have three options:

  1. Load config in the BEGIN phase and add the correct library paths before loading the actual modules
  2. Load the modules manually during BEGIN with their full path (e.g. require "/my/modules/Foo/Bar.pm"
  3. Figure out the configuration at runtime, load modules after that.

Using bare require is fairly uncomfortable, which is why Module::Runtime exists

OTHER TIPS

Use a BEGIN block to load your custom @INC location and then use lib to include it.

# use lib /a special directory/
BEGIN {
    my $lib_to_include = ...;

    require lib;
    lib->import($lib_to_include);
}

use Module_That_Requires_Special_Dir;

The only thing to note is that whatever code you use to load your custom include directory will have to rely on methods already defined before this BEGIN block. Therefore you can't use a subroutine that is later in the file.

Came across only, which seems to let a path be passed to the use argument like so:

use only { versionlib => '/home/ingy/modules' },
        MyModule => 0.33;

It has to be used with a version condition, but putting this here anyways since it's relevant to my question 1 and I wasn't able to find any modules first time around that allowed a path outside @INC.

require supposedly is able to take in a full path, according to the perlfaq with

require "$ENV{HOME}/lib/Foo.pm"; # no @INC searching!
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top