views:

270

answers:

4

I have been given a few Perl scripts to deploy.

What is the easiest way to find and install all modules used by these scripts?

EDIT:

From what I can find there are no conditional includes or includes in evals.

+2  A: 

Well this is the very simplistic way I solved this.

In a bash shell:

cat *.pl  | grep "^use " | tr ';' ' ' | while read a b c; do echo $b; done | sort -iu > modules.txt

This gave me a file with only the module names, one on each line.

I then used this

cat modules.txt | while read a; do cpan $a; done

to invoke cpan to each module name in the file. And then sat there answering yes to CPAN's questions to install dependencies as appropriate.

Not pretty but this time it got the job done.

Nifle
How about modules that aren't use'd but require'd?
innaM
I'd adjust the grep in the first line accordingly
Nifle
What about lines where `use` isn't the first thing on the line? It doesn't even allow optional spaces.
Brad Gilbert
You can of course make the grep statement as complicated as you want.I **said** that it was a *very simplistic way* I used to do **this particular job**
Nifle
+4  A: 

I was hoping Module::ScanDeps which provides the command line utility scandeps.pl would be useful here but, to my dismay, Module::ScanDeps is apparently not intended for this particular purpose as scandeps.pl either ignores missing modules or (with -c or -x) croaks when the script uses a module that is not installed.

Here is a quick'n'dirty Perl script that tries to execute the script using do until it succeeds:

#!/usr/bin/perl

use strict;
use warnings;

use Term::Prompt;

my ($script) = @ARGV;

die "Provide script file name on the command line\n"
    unless defined $script;

until ( do $script ) {
    my $ex = $@;
    if ( my ($file) = $ex =~ /^Can't locate (.+?) in/ ) {
        my $module = $file;
        $module =~ s/\.(\w+)$//;
        $module = join('::', split '/', $module);
        print "Attempting to install '$module' via cpan\n";
        system(cpan => $module);
        last unless prompt(y => 'Try Again?', '', 'n');
    }
    else {
        die $ex;
    }
}

If you do not want the script to be run, you can run perl -c $script, capture stderr output of that and parse for missing module messages and call cpan for each such module found until perl -c $script outputs "Syntax OK". That gives you a cleaner loop too. I'll look at this later.

You might miss dependencies loaded at run time using this technique.

Sinan Ünür
Isn't this a bit dangerous? The $script might do something disruptive.
Nifle
Aren't you trying to run the script? By this point, you must have decided that the script is worth running and you just need the dependencies.
Sinan Ünür
I was told to run these every day at 3 in the morning (they do some db maintenance). It is not for me to decide if it's OK to run them during the day just to get the dependencies installed. (And I don't want to wait until 3 in the morning either). I agree that most times this is probably not an issue. That's why I pointed out that it might be **a bit** dangerous.
Nifle
@Nifle Run *what* every day at 3 am? Are a bunch of people putting scripts on the machine and is your script supposed to figure out the modules they depend on? If that is the case, I would just require users to submit a set of modules which their scripts need. I thought this was a one off thing.
Sinan Ünür
As my question says *I have been given a few Perl scripts to deploy. What is the easiest way to find and install all modules used by these scripts?* So yes *MY* solution was a one off thing. Your is much better. But I still just wanted to point out that using your solution will run the script at least once. And that might not be always be a good thing.
Nifle
@Nifle We are going around in circles. It is not possible to figure out **all** dependencies of an arbitrary script without running it. But that aside, let's say your `grep` solution worked always and you installed all the modules found. I thought the whole reason you did that was so the script(s) you were given could be run. One way or another, those scripts are going to be run. If there is a chance the scripts might be nefarious, you should not be worried about dependencies in the first place.
Sinan Ünür
A: 

Or let PAR's pp do the work for you in collecting together everything you need in a single executable.

xcramps
I do not think `pp` installs missing dependencies. Am I missing something.
Sinan Ünür
+4  A: 

Does my Module::Extract::Use help? It should be able to pull out the modules named in the various import statements and give you a list.

Once you have that list, which is only the first level of dependencies, you can make a script distribution that allows people to use the normal CPAN toolchain to install everything.

brian d foy
Looks promising
Nifle