I tested my solution from a module too and it's unlikely such a chdir happens at compile time before a module is used.
Especially since a BEGIN { chdir ".." } also effects relative paths in @INC.
Granted I was too lazy posting two files.
So I'd speculate this approach is good enough for the OPs cases.
Modules can be loaded at run time. And that will break any code that relies on a constant working directory.
Load a simple module at run time from a simple script and have the module report where the main script is:
/tmp/perlmonks>cat run.pl
#!/usr/bin/perl
use strict;
use warnings;
use lib '/tmp/perlmonks';
# Tons of work omitted ... ;-)
@ARGV and chdir '/tmp/unrelated';
require Mod;
Mod::where();
/tmp/perlmonks>cat Mod.pm
package Mod;
use v5.12; # for say
use strict;
use warnings;
use FindBin;
sub where
{
say "Script is in $FindBin::Bin";
say "Script basename is $FindBin::Script";
say "Real script directory is $FindBin::RealBin";
say "Real script basename is $FindBin::RealScript";
say "Module was loaded from $INC{'Mod.pm'}";
}
1;
/tmp/perlmonks>perl run.pl
Script is in /tmp/perlmonks
Script basename is run.pl
Real script directory is /tmp/perlmonks
Real script basename is run.pl
Module was loaded from /tmp/perlmonks/Mod.pm
/tmp/perlmonks>
That works even from the command line:
/tmp/perlmonks>perl -MMod -e Mod::where
Script is in /tmp/perlmonks
Script basename is -e
Real script directory is /tmp/perlmonks
Real script basename is -e
Module was loaded from Mod.pm
/tmp/perlmonks>
Run the script again, but this time, change the working directory before loading the module:
/tmp/perlmonks>perl run.pl x
Cannot find current script 'run.pl' at /usr/share/perl5/FindBin.pm lin
+e 166.
BEGIN failed--compilation aborted at /usr/share/perl5/FindBin.pm line
+166.
Compilation failed in require at /tmp/perlmonks/Mod.pm line 6.
BEGIN failed--compilation aborted at /tmp/perlmonks/Mod.pm line 6.
Compilation failed in require at run.pl line 11.
/tmp/perlmonks>
Obviously, FindBin has some tests to prevent accidents, but they can be defeated:
/tmp/perlmonks>touch /tmp/unrelated/run.pl
/tmp/perlmonks>perl run.pl
Script is in /tmp/unrelated
Script basename is run.pl
Real script directory is /tmp/unrelated
Real script basename is run.pl
Module was loaded from /tmp/perlmonks/Mod.pm
/tmp/perlmonks>
Now, FindBin is sure that the main script is /tmp/unrelated/run.pl. It is not.
To fix that problem, load FindBin as early as possible from the main script, or even via command line parameters:
/tmp/perlmonks>perl -MFindBin run.pl x
Script is in /tmp/perlmonks
Script basename is run.pl
Real script directory is /tmp/perlmonks
Real script basename is run.pl
Module was loaded from /tmp/perlmonks/Mod.pm
/tmp/perlmonks>
Oh, by the way, this is not the problem from the FindBin documentation. FindBin documents a different problem, with persistent environments like mod_perl:
KNOWN ISSUES
If there are two modules using FindBin from different directories under the same interpreter, this won't work. Since FindBin uses a BEGIN block, it'll be executed only once, and only the first caller will get it right. This is a problem under mod_perl and other persistent Perl environments, where you shouldn't use this module. Which also means that you should avoid using FindBin in modules that you plan to put on CPAN.
Alexander
--
Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
|