CleanCode Perl Libraries
Multi-Lingual Library Maintainability
available: Perl not available: Java not available: JavaScript not available: Certified
Class
not available: Testable
Class
not available: Standalone
Mode
not available: Diagnostic
Enabled

NAME

plumb - Static Perl include file analyzer: plumb the depths of the included module hierarchy for specified Perl files.

SYNOPSIS

plumb [ -esdhH ] [ -l<level> ] assignments perl files

OPTIONS

-d

Print a detailed report, itemizing source file, line number, and package, instead of the default hierarchical report.

-e

Expand output to show all occurrences of a file

-l <level>

Limit the probe to the depth specified (default is unlimited).

-s

Sort output by file name (first field).

-h

Option help.

-H

Long help (manual page).

assignments

variable_name=value - plugs in values for specified variables before running.

perl files

One or more files to plumb.

REQUIRES

Perl5.005, Getopt::Std, Pod::Usage, File::Basename, Data::Handy

DESCRIPTION

Plumb the depths of the included module hierarchy for specified Perl files, providing a simple way to determine a module dependency tree. For a simple program, this won't reveal too much, but once you have dozens or hundreds of interlinked files, this can help track down dependency issues. This program is adapted from a version for C code I created when I worked at one firm which used a few libraries. Checking the dependencies on a single file produced a list of 17,000+ dependent files (with duplication, though)!

Use this module to:

Track down where a module is called from

When I was new to a project (and a company) I needed to see who was calling a particular module because that module was invoking a second module when I didn't think it should be. Going to my ~/web/cgi-bin directory, and invoking plumb codedir/* showed me immediately where that first module was being called from, answering my question.

Determine which files to include in a release

Run plumb, filter out the lines which refer to system files, then you've got a list of the files being used.

See which system modules are invoking others

Perhaps of more academic curiousity, plumb doesn't stop at user files. It will also show you Perl system file dependencies.

Input affects output

Your current directory, command-line file specification, and how your use lib directives are written may affect your output.

While the following two commands might appear to be equivalent, I get different results:

        (A1) cd ~/web/cgi-bin; plumb codedir/*
        (A2) cd ~/web/cgi-bin/codedir; plumb *

By "different", I mean that some include files won't be found, denoted as ???/path/file in the output, and therefore won't be expanded further.

Why? Plumb traverses included files by examining the use lib directives. Consider these two lines:

        (B1) use lib "../../foo::bar"
        (B2) use lib "foo::bar"', etc.

While they may resolve to the same library path (depending on @INC), they may cause some differences in plumb output. If you use relative paths as in line (B1) above, you need to sit in the same directory as your target when you run plumb.

So if you have a lot of ??? items showing up, make sure you're in the same directory as your target, as in line (A2) above.

Static vs. Dynamic lookup

If you use only static include directives, (i.e. hardcoded lines like use File::stat) then invoking plumb with a single file name is usually sufficient, as in:

        plumb myfile.pl

But if you use dynamic module loading, there are a couple things you could do. If you use a require statement with a variable name, as in:

        require $newModule;

plumb lets you specify a value for the variable $newModule on the command line:

        plumb newModule=lexScanner.pm myfile.pl

Then lexScanner.pm will be substituted in each file before examining it. You may specify multiple name/value pairs, but only one value per variable name. So if you have a loop which invokes require on several modules, you can only tell plumb about one of them. In this latter case, your next best solution is to invoke plumb with multiple files as in, for example:

        plumb myfile.pl libs/lex*.pm

So if scanning myfile.pl statically won't connect to the other library files, simply ask plumb to scan them as well.

Examples

In this first example, we want to find out what is included in the file ExecJava.pm, both its direct descendants and every nested descendant. The output shows the hierarchy by the indentation. That is, strict, vars, and Data::Diagnostic may all be found as explicitly included in this file. We further observe that vars.pm includes register.pm, which includes warnings.pm, which includes Carp.pm, and so forth. Note that the next time vars.pm is encountered (just under Data/Diagnostic.pm) it is not further plumbed -- there's no need, as we have already done it once. This keeps the list as concise as possible.

        % plumb.pl ExecJava.pm

   C:/Perl/lib/strict.pm
   C:/Perl/lib/vars.pm
      C:/Perl/lib/warnings/register.pm
         C:/Perl/lib/warnings.pm
            C:/Perl/lib/Carp.pm
               C:/Perl/lib/Exporter.pm
                  C:/Perl/lib/Exporter/Heavy.pm
                     C:/Perl/lib/strict.pm
                     C:/Perl/lib/Exporter.pm
                     C:/Perl/lib/Carp.pm
      C:/Perl/lib/strict.pm
      C:/Perl/lib/Carp.pm
   C:/usr/ms/devel/perl/lib/Data/Diagnostic.pm
      C:/Perl/lib/strict.pm
      C:/Perl/lib/vars.pm
      C:/usr/ms/devel/perl/lib/Data/Handy.pm
         C:/Perl/lib/strict.pm
         C:/Perl/lib/vars.pm
         C:/Perl/lib/File/stat.pm
            C:/Perl/lib/strict.pm
            C:/Perl/lib/warnings.pm
            C:/Perl/lib/Exporter.pm
            C:/Perl/lib/vars.pm
            C:/Perl/lib/Class/Struct.pm
               C:/Perl/lib/strict.pm
               C:/Perl/lib/warnings/register.pm
               C:/Perl/lib/Carp.pm
               C:/Perl/lib/Exporter.pm
        (another 150 lines follow...)

The file run.pl contains a line require $itemName.pm so we instantiate the variable. Also, we know that run.pl uses all the .pm files under codedir/ but it uses an indirect method, so we just enumerate codedir/*.pm on the command line to cover them all. In this example, we illustrate the other format available with the -d option. Here, rather than show the visual hierarchy, the program shows a textual hierarchy.

        % plumb.pl -d itemName=codedir run.pl codedir/*.pm
        run.pl                              (line   5, pkg main     ) ==> ../../libs/Mod.pm
        ../../libs/Mod.pm                   (line  74, pkg Node     ) ==> ../../libs/DEBUG.pm
        ../../libs/Mod.pm                   (line 195, pkg Mod      ) ==> ../../libs/HTML_Page.pm
        ../../libs/HTML_Page.pm             (line   2, pkg HTML_Page) ==> ../../libs/perl5/lib/HTML/Template.pm
        ../../libs/Mod.pm                   (line 196, pkg Mod      ) ==> ../../libs/perl5/lib/Time/HiRes.pm
        ../../libs/perl5/lib/Time/HiRes.pm  (line   6, pkg main     ) ==> /usr/local/lib/perl5/Exporter.pm
        /usr/local/lib/perl5/Exporter.pm    (line  19, pkg Exporter ) ==> /usr/local/lib/perl5/Carp.pm
        /usr/local/lib/perl5/Carp.pm        (line  55, pkg Carp     ) ==> /usr/local/lib/perl5/Exporter.pm
 . . .

EXIT STATUS

Return Code 0

Normal exit.

Return Code 1

Missing file name argument(s).

BUGS

None

AUTHOR

Michael Sorens

VERSION

$Revision: 8 $ $Date: 2006-12-19 21:13:43 -0800 (Tue, 19 Dec 2006) $

SINCE

CleanCode 0.9


CleanCode Perl Libraries Copyright © 2001-2013 Michael Sorens - Revised 2013.06.30 Get CleanCode at SourceForge.net. Fast, secure and Free Open Source software downloads