locked_user sundialsvc4 has asked for the wisdom of the Perl Monks concerning the following question:

I've finally finished (yay!) a big fat hairy program that'll be a great web-site ... as soon as I can successfully get it installed on the target web-server with all of its dependencies.

Okay, then... how do I make a distribution package to do that ... “automagically?”

I want to have the tool scan the source code for “uses” and “require” statements, and generate a nice package that will automatically retrieve from CPAN and install the packages that this app needs. “DWIM,” y’know...

It's gotta be there ... I just haven't done it before. Pointers please?

Replies are listed 'Best First'.
Re: Making a distribution for my website
by Your Mother (Archbishop) on Feb 06, 2009 at 06:58 UTC
    I want to have the tool scan the source code for “uses” and “require” statements, and generate a nice package that will automatically retrieve from CPAN and install the packages that this app needs. “DWIM,” y’know...

    I would recommend against that, though it would be nice. It is not self-documenting. Too much voodoo.

    Module::Install is really very nice and self-bundling. I.e., MI does not have to be installed on the target machine. It's good at pulling in CPAN deps and such when you run its Makefile.PL. And that is where the real task is. The Perl makefile is just a script. They come with lots of features from the various installers but they're still just scripts. I did one recently that fetched zipped dists of thirdparty JS stuff and installed it automatically when the makefile was run. That way no external libs are in the source control; only in the dist. Again, kudos to adamk is due. He maintains the archive zip stuff too, I think.

    The other good thing about makefiles is they document their requirements both for man and machine. If you do end up writing a scanning script, have it write your makefile requires statements for you. Best of both. Share it if you do!

    Oh, congratulations, too! Feels great to get a project done well.

Re: Making a distribution for my website
by gone2015 (Deacon) on Feb 06, 2009 at 18:23 UTC

    If you're trying to find out what modules your program is using, then the following piece of magic may suffice:

    END { foreach my $file (sort keys %INC) { my $module = $file ; my $located = $INC{$file} ; $module =~ s|\.pm$|| ; $module =~ s|[/\\]|::|g ; my $version ; { no strict 'refs' ; $version = ${$module.'::VERSION'} || '??' ; } + ; print STDERR "$module v$version $located\n" ; } ; } ;
    if inserted at the start of your program, just before it ends it will print to STDERR all the module file names it has seen require and/or use for. Which may give you a fighting chance of constructing a suitable install package.

    Mind you, this will miss any require operations which your test run does not actually execute... I cannot imagine that this will be a problem, but it's tricky to guarantee. If that worries you, you can try:

    END { my %myINC = () ; foreach my $file (keys %INC) { my $module = $file ; $module =~ s|\.pm$|| ; $module =~ s|[/\\]|::|g ; $myINC{$module} = $INC{$file} ; } ; foreach my $module (sort keys %myINC) { my $located = $myINC{$module} ; my $version ; { no strict 'refs' ; $version = ${$module.'::VERSION'} || '??' ; } + ; print STDERR "$module v$version $located\n" ; open my $FH, "<", $located or die "failed to open $located: $!" +; while (<$FH>) { if ((m/^(?:\s+|[^#;];\s*)*?require\s+([A-Za-z]\w+(?:::\w+)*)\b/) && !exists($myINC{$1 +})) { printf STDERR " %5d: %s", $., $_ ; } ; } ; } ; } ;
    which has a stab at scanning all the required files, looking for require statements for modules (ignores version numbers) that refer to modules that are not already known.

    NB: this comes without warranty of any kind, in particular no warranty of fitness for any purpose whatsoever; and if you use this you acknowledge that such exclusion of warranty is entirely reasonable, given the level of the fee :-)

Re: Making a distribution for my website
by Bloodnok (Vicar) on Feb 07, 2009 at 01:08 UTC
    Other, splendid answers apart, the easiest way to list all files use'ed &/or require'ed is to dump the keys of the %INC hash e.g. add an option to your program (by which I'll assume you mean a script) solely to dump the said data. Thereafter it's a trivial task to get the package names ...
    my @pkgs = map { s,[/\\],::,g; s,\.pm$,,} keys %INC;

    A user level that continues to overstate my experience :-))