Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Hi. I'm trying to write a tool which would enable me to automatically determine the modules which are used when a specific module is loaded.

So, if Module::A uses Module::B which in turn uses Module::C I'd like to end up with a data structure that lets me know that Module::A uses Module::B uses Module::C in that order when Module::A is loaded.

To do that I'm trying to play around with %INC; here's what I've got so far.
sub get_inc { my $module = shift; local %INC; eval { require $module }; return keys %INC }
This allows me to inspect my local copy of %INC after a module has been required so that I can see what modules have been loaded after the fact.

However, the data returned doesn't provide any clues as to the order in which the modules were loaded. So I only know that then end result of requiring Module::A is that Module::B and Module::C were loaded; but I can't tell if Module::A loaded Module::C directly or if Module::C was loaded by Module::B.

So, what I am hoping to do is replace my local %INC with a tied hash that would allow me to inspect the contents of %INC as the modules are being 'required'.

Here are my questions:
  • Is it possible to manipulate %INC in this manner?
  • If not, is there another way to accomplish this?
  • I would like to avoid any solutions which involve the scanning of perl module files; at this point I'm hoping that the perl interpreter itself provides enough hooks to extract this information once a module has been required.

    Replies are listed 'Best First'.
    Re: Module Loading Tool
    by jasonk (Parson) on Aug 14, 2008 at 00:56 UTC

      You can put a code reference into @INC, which will be called by require when attempting to load a module. If the code reference returns undef, then require will continue on using the rest of @INC...

      unshift( @INC, sub { my ( $self, $path ) = @_; $path =~ s#/#::#g; $path =~ s/\.pm$//; print "INC: $path\n"; return undef; } );

      www.jasonkohles.com
      We're not surrounded, we're in a target-rich environment!
    Re: Module Loading Tool
    by betterworld (Curate) on Aug 14, 2008 at 00:13 UTC

      I've not tried it, but how about overriding &CORE::GLOBAL::require? From there, you could use caller() to get information about how modules use each other.

      Update: OK, now I've tried it:

      use strict; use warnings; BEGIN { *CORE::GLOBAL::require = sub { my ($mod) = @_; my $caller = caller; warn "$caller uses $mod.\n"; CORE::require($mod) unless $mod =~ /^5/; }; } use CGI;

      It's kind of quick and ugly, but seems to work.

        This solution should work... Thanks...
    Re: Module Loading Tool
    by Khen1950fx (Canon) on Aug 14, 2008 at 00:21 UTC
      Just add use Devel::Loaded to your script. It'll catch run-time loads.
    Re: Module Loading Tool
    by tod222 (Pilgrim) on Aug 14, 2008 at 01:02 UTC
      I would like to avoid any solutions which involve the scanning of perl module files...

      Is this for reasons of efficiency?

      If not, another option is to use Module::ScanDeps in your code, or scandeps.pl from the command line.

      Since I'm not clear on your requirements this suggestion may not be what you need, but I wanted to be sure you were aware of the option.