local::lib and File::Spec
local::lib
is a great way of providing a contained set of Cpan modules for an application. But it can be awkward if you wish to use it to provide an updated File::Spec
.
I wished to use Path::Tiny
, which I installed using cpanm
to a local::lib
directory. That requires a newer version of File::Spec
than came with the Perl we're using, so cpanm
downloaded and installed that to the local::lib
directory too, just as you'd want.
Our programs specify their local::lib
directory, so they can be run by users without them having to fiddle around setting up environment variables (on Windows). Unfortunately, however, using it in our programs like this didn't work for Path::Tiny
:
use local::lib qw<Cpan>;
use Path::Tiny;
That complains “File::Spec version 3.4 required--this is only version 3.33”.
I'm grateful to Haarg on #perl-help for pointing out that local::lib
itself uses File::Spec
(which isn't at all surprising, when you think about it), so will have loaded the system-installed version of File::Spec
before it gets round to adding the directory where the new one is found to the search path. When Path::Tiny
then uses File::Spec
, the older one is already loaded, which means perl
doesn't go looking for a newer one.
Apparently what I should've been doing is this instead:
use lib qw<Cpan/lib/perl5/>;
use Path::Tiny;
That adds the relevant local::lib
directories first, without actually loading the local::lib
module or any of its dependencies such as File::Spec
.
This works even though File::Spec
is an XS module, so is actually found in a subdirectory called something like Cpan/lib/perl5/MSWin32-x86-multi-thread: the use lib
automatically adds the architecture-specific directory as well, which is handy.
(The above was my fourth attempt at a workaround. Not knowing about that lib
feature, my third attempt involved messing around with $Config{archname}
to build the subdirectory path.
My second attempt was to unload the File::Spec
that local::lib
had loaded:
use local::lib 'Cpan';
BEGIN
{
delete @INC{glob 'File/Spec{,/Unix,/Win32}.pm'};
no strict qw<refs>;
%{'File::Spec::'} = ();
}
use Path::Tiny;
It, erm, works, but doesn't exactly strike me as code we should have in production.
My first attempt tried to use Class::Unload
. I now think that would've worked, but I was copying the example in its synopsis, which uses Class::Inspector
to report whether the class has been unloaded — which itself uses File::Spec
!)
Updated Tweaked after MST explained a little more about local::lib
. And another workaround attempt added (and the others renumbered) after Ribasushi reminded me I'd tried Class::Unload
.