#!/usr/bin/perl -w # # $Id$ # Copyright B. Cornec 2005-2013 # Provided under the GPL v2 # # Get perl modules required by mindi and mondo and that should be on the restore media # use strict; use File::Find; use MondoRescue::File; =pod =head1 NAME mindi-get-perl-modules keeps track of the perl modules that should be on your restore media =head1 DESCRIPTION mindi-get-perl-modules keeps track of the perl modules that should be on your restore media by analyzing the references made to them in the scripts passed as parameters and returning all the modules needed to have them work correctly. =head1 SYNOPSIS mindi-get-perl-modules /path/to/perl-script ... =head1 ARGUMENTS =over 4 =item B This is the path of the perl script to analyze and for which we want its perl modules in dependence to be included =back =head1 WEB SITES The main Web site of the project is available at L. Bug reports should be filled using the trac instance of the project at L. =head1 USER MAILING LIST For community exchanges around MondoRescue please use the list L =head1 AUTHORS The MondoRescue team lead by Bruno Cornec L. =head1 COPYRIGHT MondoRescue is distributed under the GPL v2.0 license or later, described in the file C included with the distribution. =cut my $file = mr_get_perl_modules(@ARGV) if (defined $ARGV[0]); foreach my $f (sort keys %$file) { print "$f\n"; } sub mr_get_perl_modules { my %files; #print "Searching in "; #print join "\n", @INC; my $require = mr_process_file(@_); my @includes; # Remove non exiting directories from @INC # and thus avoid perl warnings # foreach my $d (@INC) { $d = mr_file_read_all_link($d) if (-l $d); push @includes,$d if (-d $d); } find( sub { if ((-f $File::Find::name) && (/\.pm$/) && (not defined $files{$File::Find::name})) { foreach my $m (keys %$require,"warnings") { (my $mod = $m) =~ s|::|/|g; #print "Looking at $mod in $File::Find::name\n"; if (index($File::Find::name,"$mod.pm") ne -1) { $files{$File::Find::name} = $mod; #push @files, $File::Find::name; #print "Found $mod in $File::Find::name\n"; last; } } } }, @includes); return(\%files); } # Adapted From /usr/lib/rpm/mandriva/perl.req # by Ken Estes Mail.com kestes@staff.mail.com # used under the GPL sub mr_process_file { my %line; my %require; my $current_line; my $tag; foreach my $file (@_) { open(FILE, "<$file") || return(\%require); while () { # skip the "= <<" block if ( ( m/^\s*\$(.*)\s*=\s*<<\s*["'](.*)['"]/i) || ( m/^\s*\$(.*)\s*=\s*<<\s*(.*);/i) ) { $tag = $2; while () { ( $_ =~ /^$tag/) && last; } } # skip the documentation # we should not need to have item in this if statement (it # properly belongs in the over/back section) but people do not # read the perldoc. if ( (m/^=(head1|head2|pod|item)/) .. (m/^=(cut)/) ) { next; } if ( (m/^=(over)/) .. (m/^=(back)/) ) { next; } # skip the data section if (m/^__(DATA|END)__$/) { last; } # Each keyword can appear multiple times. Don't # bother with datastructures to store these strings, # if we need to print it print it now. if ( m/^\s*\$RPM_Requires\s*=\s*["'](.*)['"]/i) { foreach $_ (split(/\s+/, $1)) { print "$_\n"; } } if ( # ouch could be in a eval, perhaps we do not want these since we catch # an exception they must not be required # eval { require Term::ReadLine } or die $@; # eval "require Term::Rendezvous;" or die $@; # eval { require Carp } if defined $^S; # If error/warning during compilation, (m/^(\s*) # we hope the inclusion starts the line (require|use)\s+(?!\{) # do not want 'do bracket' loops # quotes around name are always legal [\'\"]?([^\;\ \'\"\t]*)[\'\"]?[\t\;\ ] # the syntax for 'use' allows version requirements \s*([.0-9]*) /x) ) { my ($whitespace, $statement, $module, $version) = ($1, $2, $3,$4); my $usebase; # we only consider require statements that are flush against # the left edge. any other require statements give too many # false positives, as they are usually inside of an if statement # as a fallback module or a rarely used option ($whitespace ne "" && $statement eq "require") && next; # if there is some interpolation of variables just skip this # dependency, we do not want # do "$ENV{LOGDIR}/$rcfile"; ($module =~ m/\$/) && next; # skip if the phrase was "use of" -- shows up in gimp-perl, et al next if $module eq 'of'; # if the module ends in a comma we probaly caught some # documentation of the form 'check stuff,\n do stuff, clean # stuff.' there are several of these in the perl distribution ($module =~ m/[,>]$/) && next; # if the module name starts in a dot it is not a module name. # Is this necessary? Please give me an example if you turn this # back on. # ($module =~ m/^\./) && next; # if the module ends with .pm strip it to leave only basename. # starts with /, which means its an absolute path to a file if ($module =~ m(^/)) { print "$module\n"; next; } # as seen in some perl scripts # use base qw(App::CLI Class::Accessor::Chained::Fast App::CLI::Command); if ($module eq 'base') { $require{$module} = $version; $line{$module} = $current_line; ($module = $_) =~ s/use\s*base\s*//; $module =~ s/qw\((.*)\)\s*;/$1/; $module =~ s/qw(.)(.*)\1\s*;/$2/; $module =~ s/\s*;$//; $module =~ s/#.*//; $usebase = 1; } # sometimes people do use POSIX qw(foo), or use POSIX(qw(foo)) etc # we can strip qw.*$, as well as (.*$: $module =~ s/qw.*$//; $module =~ s/\(.*$//; $module =~ s/\.pm$//; # some perl programmers write 'require URI/URL;' when # they mean 'require URI::URL;' $module =~ s/\//::/; # trim off trailing parenthesis if any. Sometimes people pass # the module an empty list. $module =~ s/\(\s*\)$//; # if module is a number then both require and use interpret that # to mean that a particular version of perl is specified. Don't # add a dependency, though, since the rpm will already require # perl-base at the build version (via find-requires) next if $module =~ /^v?\d/; # ph files do not use the package name inside the file. # perlmodlib documentation says: # the .ph files made by h2ph will probably end up as # extension modules made by h2xs. # so do not spend much effort on these. # there is no easy way to find out if a file named systeminfo.ph # will be included with the name sys/systeminfo.ph so only use the # basename of *.ph files ($module =~ m/\.ph$/) && next; # if the module was loaded trough base, we need to split the list if ($usebase) { my $current_line = $_; foreach (split(/\s+/, $module)) { next unless $_; $require{$_} = $version; $line{$_} = $current_line; } } else { $require{$module}=$version; $line{$module}=$current_line; } } } close(FILE) || die("$0: Could not close file: '$file' : $!\n"); } return(\%require); }