views:

178

answers:

2

I'd like to get the full filename of an included module. Consider this code:

package MyTest;

my $path = join '/', split /::/, __PACKAGE__;
$path .= ".pm";

print "$INC{$path}\n";

1;

$ perl -Ipath/to/module -MMyTest -e0
path/to/module/MyTest.pm

Will it work on all platforms?

perlvar

The hash %INC contains entries for each filename included via the do, require, or useoperators. The key is the filename you specified (with module names converted to pathnames), and the value is the location of the file found.

Are these keys platform-dependent or not? Should I use File::Spec or what? At least ActivePerl on win32 uses / instead of \.

Update: What about %INC values? Are they platform-dependent?

+2  A: 

Given that it's a standard module, go with the approach from Module::Loaded:

sub is_loaded (*) { 
    my $pm      = shift;
    my $file    = __PACKAGE__->_pm_to_file( $pm ) or return;

    return $INC{$file} if exists $INC{$file};

    return;
}

sub _pm_to_file {
    my $pkg = shift;
    my $pm  = shift or return;

    my $file = join '/', split '::', $pm;
    $file .= '.pm';

    return $file;
}
Greg Bacon
Actually this is the very same code I wrote. And it seems, that this module works on most platforms. Thank you. http://www.cpantesters.org/distro/M/Module-Loaded.html#Module-Loaded-0.06 Can you say anything about portability of `%INC` values?
codeholic
@codeholic Which platforms do you have in mind? Remember that `/` is a valid path separator on Win32, and much nicer looking than obnoxious double-backwhacks. Don't let the Windows command shell's geb0rken *interface* mislead you.
Greg Bacon
I want to make as cross-platform code as possible, since I'm looking forward to post it on CPAN finally.
codeholic
/ is not a valid seperator on VMS (at least at an OS level - Perl can handle these and convert to the OS format).
justintime
+1  A: 

Here's a reasonably robust implementation that will also work for modules that haven't been loaded yet.

use File::Find;
use File::Spec;

sub pkg2path (*) {
    my $file = join '[\\\/:]' =>
               map  "\Q$_"    =>
               split /::|'/   => "$_[0].pm";            # '

    /$file$/ and return File::Spec->rel2abs( $INC{$_} )
        for keys %INC;

    # omit the rest to only find loaded modules

    my $path; find {
        no_chdir => 1,
        wanted   => sub {
            $path = $_ and goto found if /$file$/
        }
    } => @INC;

    found: File::Spec->rel2abs($path or return)
}

say pkg2path Benchmark;
say pkg2path Devel::Trace;
Eric Strom
The same code is written better in `Module::Find`. But it doesn't answer my question.
codeholic
the first line of pkg2path has your answer. no, its not safe to trust that the entries in %INC will have a particular pattern (if only because %INC is user writable). That's why I wrote the regex builder (which is not found in Module::Find, and it doesn't even use %INC btw). Nice to know you appreciate the few people that took the time to think about and answer your question
Eric Strom