#!/usr/bin/perl -w # # Get perl modules required by mindi and mondo and that should be on the restore media # use strict; use File::Find; use Cwd; my $file = get_perl_modules(@ARGV) if (defined $ARGV[0]); foreach my $f (sort keys %$file) { print "$f\n"; } sub get_perl_modules { my %files; #print "Searching in "; #print join "\n", @INC; my $require = process_file(@_); my @includes; # Remove non exiting directories from @INC # and thus avoid perl warnings # foreach my $d (@INC) { $d = 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); } # Cf: http://www.stonehenge.com/merlyn/UnixReview/col27.html sub read_all_link { my $dir = cwd; my $link; find sub { return unless -l; my @right = split /\//, $File::Find::name; my @left = do { @right && ($right[0] eq "") ? shift @right : # quick way split /\//, $dir; }; # first element always null while (@right) { my $item = shift @right; next if $item eq "." or $item eq ""; if ($item eq "..") { pop @left if @left > 1; next; } my $link = readlink (join "/", @left, $item); if (defined $link) { my @parts = split /\//, $link; if (@parts && ($parts[0] eq "")) { # absolute @left = shift @parts; # quick way } unshift @right, @parts; next; } else { push @left, $item; next; } } #print "$File::Find::name is ", join("/", @left), "\n"; $link = join("/", @left); }, @_; return($link); } # Adapted From /usr/lib/rpm/mandriva/perl.req # by Ken Estes Mail.com kestes@staff.mail.com sub 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 {' 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); }