Wednesday, April 7, 2010

use libs $FindBin::Bin considered harmful

It's time to stop using FindBin::Bin to find the location of our perl programs, when we already have that in $0. If we aren't going to use FindBin::Bin to prime the libs directory, what should we be using to pull in relative directories?

The FindBin version:
Begin{ use FindBin::Bin; use lib "$FindBin::Bin/../lib"; }

A File::Basename + Cwd version:
use Cwd qw(abs_path);
use File::Basename qw(dirname);
use lib dirname(abs_path($0)) . '/../../../../lib/perl';

A File::Spec + File::Basename version:
use File::Basename qw(dirname);
use File::Spec;
use lib dirname(File::Spec->rel2abs( $0 )) . '../../../../lib/perl';

Quoting a chunk of Tye's post from the perlmonks discussion.

Well, I've complained about this particular module before because I think the perverse way in which it was implemented is quite unfortunate because it makes it nearly impossible to use it for lots of useful things like finding other programs that might be on your PATH. (After all, it is called FindBin and $0 is a script, not a binary.)

But when this question came up in chatterbox, I did some checking and it appears to me that the correct answer for the full path to the current script is simply: rel2abs( $0 ) as another answer mentions.

For those that don't know, the (perverse) FindBin module goes through some effort to search $ENV{PATH} trying to find your script. However, I can't come up with any situation in which the script would have been found on the $ENV{PATH} but that $0 would not contain the (full) path, so the module appears to be pretty useless.

-- Tye on perlmonks.org

Posting, since I can never seem to find this snippet when I need it.

I'm really only interested in the case of automatically pulling in my modules from my test directory, for work code. When writing modules, I use Dist::Zilla and dzil test to set the paths. When running random .t files, I use FindBin::libs to automatically pull in libs/ directories from farther up the path. (which uses FindBin, but is really handy). FindBin::libs source

3 comments:

Jakub Narebski said...

Yet another solution would be to use Dir::Self, which provides __DIR__ constant, analogous to __FILE__ constant.

Anonymous said...

The File::Spec version needs a dirname() call.

Andrew Grangaard said...

Thanks Anonymous!

I've updated the example.