source: MondoRescue/branches/3.2/mindi/mindi-get-perl-modules@ 3390

Last change on this file since 3390 was 3390, checked in by Bruno Cornec, 9 years ago
  • mindi logs now the perl modules found
  • lib is also a useful perl module not automatically detected at least on CentOS6
  • Property svn:executable set to *
File size: 9.8 KB
RevLine 
[2183]1#!/usr/bin/perl -w
2#
[3143]3# $Id$
4# Copyright B. Cornec 2005-2013
5# Provided under the GPL v2
6#
[2183]7# Get perl modules required by mindi and mondo and that should be on the restore media
8#
9use strict;
10
[3223]11use MondoRescue::File;
[3385]12use Data::Dumper;
13use File::Basename;
[3386]14use ProjectBuilder::Base;
15use Getopt::Long qw(:config auto_abbrev no_ignore_case);
[3388]16use Module::ScanDeps;
[2183]17
[3143]18=pod
19
20=head1 NAME
21
22mindi-get-perl-modules keeps track of the perl modules that should be on your restore media
23
24=head1 DESCRIPTION
25
26mindi-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.
27
28=head1 SYNOPSIS
29
30mindi-get-perl-modules /path/to/perl-script ...
31
32=head1 ARGUMENTS
33
34=over 4
35
36=item B</path/to/perl-script>
37
38This is the path of the perl script to analyze and for which we want its perl modules in dependence to be included
39
40=back
41
42=head1 WEB SITES
43
44The main Web site of the project is available at L<http://www.mondorescue.org>. Bug reports should be filled using the trac instance of the project at L<http://trac.mondorescue.org/>.
45
46=head1 USER MAILING LIST
47
48For community exchanges around MondoRescue please use the list L<http://sourceforge.net/mailarchive/forum.php?forum_name=mondo-devel>
49
50=head1 AUTHORS
51
52The MondoRescue team lead by Bruno Cornec L<mailto:bruno@mondorescue.org>.
53
54=head1 COPYRIGHT
55
56MondoRescue is distributed under the GPL v2.0 license or later,
57described in the file C<COPYING> included with the distribution.
58
59=cut
60
[3386]61# Globals
[3385]62my %files;
63my %found;
[3386]64my %opts;
65my @includes;
66my $req;
67GetOptions(
68 "verbose|v+" => \$opts{'v'},
69);
[2183]70
[3386]71pb_syntax_init("mindi-get-perl-modules Version PBVER-rPBREV\n");
72
73$pbdebug = $opts{'v'};
74$pbdebug = 0 if (not defined $pbdebug);
75
76# Remove non exiting directories from @INC
77# and thus avoid perl warnings
78#
79pb_log(1,"Searching originally in ");
80pb_log(1,join " - ", @INC);
81pb_log(1,"\n");
82
83foreach my $d (@INC) {
84 $d = mr_file_read_all_link($d) if (-l $d);
85 push @includes,$d if (-d $d);
86}
87
88pb_log(1,"Searching finally in ");
89pb_log(1,join " - ", @INC);
90pb_log(1,"\n");
91
92# deduplicate entry list
[3388]93if (not defined $ARGV[0]) {
[3386]94 pb_syntax();
95}
96
97pb_temp_init();
98
[3389]99my $tmpf = "$ENV{'PBTMP'}/mpgm.$$.pl";
[3386]100
101open(TMPF,"> $tmpf") || die "Unable to create $tmpf: !$\n";
102print TMPF "# To include module corresponding to perl -w\n";
103print TMPF "use warnings;\n";
104print TMPF "# To include module used by Data:Dumper in a masked way for this script\n";
105print TMPF "use bytes;\n";
[3389]106print TMPF "# To prevent a bug with perl 5.10.0\n";
[3390]107print TMPF "use Tie::Hash::NamedCapture;\n";
108print TMPF "# To prevent a bug somewhere with scan_deps :-(\n";
109print TMPF "use lib;\n";
[3386]110close(TMPF);
111
[3388]112push @ARGV,$tmpf;
113
114my $h = scan_deps(
115 files => \@ARGV,
116 recurse => 1,
117 );
118
[3386]119unlink("$tmpf");
120
[3388]121pb_log(1,"Returning:");
122pb_log(1,Dumper($h));
123pb_log(1,"-----------------------\n");
124foreach my $f (sort keys %$h) {
[3389]125 # Skip the temp file
126 next if ($h->{$f}->{'file'} =~ /$ENV{'PBTMP'}/);
[3388]127 print "$h->{$f}->{'file'}\n";
[2183]128}
129
[3383]130exit 0;
131
[3223]132sub mr_get_perl_modules {
[2183]133
[3386]134my $f = shift;
[3385]135my %newly_found;
[3386]136my $require;
[2183]137
[3386]138return($require) if (not defined $f);
139$require = mr_process_file($f);
[2183]140
[3386]141pb_log(1,"Requires on $f found:\n");
142pb_log(1,Dumper($require));
143pb_log(1,"---------------\n");
[2183]144
[3385]145my $cpt = keys %$require;
146
147# Exit recursion
[3386]148return($require) if ($cpt == 0);
[3385]149
150foreach my $m (keys %$require) {
151 (my $mod = $m) =~ s|::|/|g;
152 # Already found
153 next if (exists $found{$mod});
154 foreach my $d (@includes) {
[3386]155 pb_log(1,"Looking at $m in $d\n");
[3385]156 if (-f "$d/$mod.pm") {
157 $files{"$d/$mod.pm"} = $mod;
158 $found{$mod} = "$d/$mod.pm";
159 $newly_found{"$d/$mod.pm"} = $mod;
[3386]160 pb_log(1,"Found $mod in $d/$mod.pm\n");
[3385]161 last;
[2183]162 }
[3385]163 }
[3386]164 print STDERR "ERROR: Unable to find $mod. Your perl setup may be incorrect\n" if (not exists $found{$mod});
[3385]165}
166
[3386]167pb_log(1,"Files found:\n");
168pb_log(1,Dumper(\%files));
169pb_log(2,Dumper(\%found));
170pb_log(1,"---------------\n");
[2183]171
[3386]172pb_log(1,"New Files found:\n");
173pb_log(1,Dumper(\%newly_found));
174pb_log(1,"---------------\n");
[3385]175
[3386]176my $foundrec;
[3383]177
[3386]178$cpt = keys %newly_found;
179if ($cpt != 0) {
180 # Recurse on what we just found
181 foreach my $f (keys %newly_found) {
182 $foundrec = mr_get_perl_modules($f);
183 foreach my $k (keys %$foundrec) {
184 $files{$k} = $foundrec->{$k};
185 }
186 }
[3383]187
[3386]188 # Cleanup
189 foreach my $k (keys %newly_found) {
190 delete $newly_found{$k};
[3385]191 }
192}
193
[3386]194pb_log(1,"End of mr_get_perl_modules on $f we got:".Dumper(\%files)."\n");
195
[2183]196return(\%files);
197}
198
[3383]199# Adapted From /usr/lib/rpm/mageia/perl.req
[2183]200# by Ken Estes Mail.com kestes@staff.mail.com
[3224]201# used under the GPL
[2183]202
[3223]203sub mr_process_file {
[2183]204
[3386]205my $file = shift;
[3143]206my %line;
[3386]207my %list_req;
208my $current_line = undef;
209my $tag = undef;
[2183]210
[3386]211return(\%list_req) if (not defined $file);
[2183]212
[3386]213pb_log(2, "START Analyze $file\n");
214# In a recursive configuration we need a var as file handle
215open(my $fh, "$file") || return(\%list_req);
216while (my $line = <$fh>) {
[2183]217
[3386]218 # skip the "= <<" block
219
220 pb_log(2, "LINE: $line");
221 if ( ( $line =~ m/^\s*\$(.*)\s*=\s*<<\s*["'](.*)['"]/i) ||
222 ($line =~ m/^\s*\$(.*)\s*=\s*<<\s*(.*);/i) ) {
223 $tag = $2;
224 while (my $l = <$fh>) {
225 pb_log(2, "LINE-tag: $l");
226 ( $l =~ /^$tag/) && last;
[3143]227 }
[3386]228 }
[2183]229
[3386]230 pb_log(2, "PHASE 2: $line");
231 # skip the documentation
232
233 # we should not need to have item in this if statement (it
234 # properly belongs in the over/back section) but people do not
235 # read the perldoc.
236
237 if ( ($line =~ m/^=(head1|head2|pod|item)/) .. ($line =~ m/^=(cut)/) ) {
238 pb_log(2, "PHASE 2bis:$line");
239 next;
240 }
241 pb_log(2, "PHASE 3:\n");
242
243 if ( ($line =~ m/^=(over)/) .. ($line =~ m/^=(back)/) ) {
244 next;
245 }
246 pb_log(2, "PHASE 4:\n");
[3143]247
[3386]248 # skip the data section
249 if ($line =~ m/^__(DATA|END)__$/) {
250 last;
251 }
252 pb_log(2, "PHASE 5:\n");
[2183]253
[3386]254 # Each keyword can appear multiple times. Don't
255 # bother with datastructures to store these strings,
256 # if we need to print it print it now.
[2183]257
[3386]258 if ($line =~ m/^\s*\$RPM_Requires\s*=\s*["'](.*)['"]/i) {
259 foreach my $l (split(/\s+/, $1)) {
260 print "$l\n";
261 }
262 }
263 pb_log(2, "PHASE 6:\n");
264
265 if (
266
[2183]267# ouch could be in a eval, perhaps we do not want these since we catch
268# an exception they must not be required
269
270# eval { require Term::ReadLine } or die $@;
271# eval "require Term::Rendezvous;" or die $@;
272# eval { require Carp } if defined $^S; # If error/warning during compilation,
273
274
[3386]275 ($line =~ m/^(\s*) # we hope the inclusion starts the line
276 (require|use)\s+(?!\{) # do not want 'do bracket' loops
277 # quotes around name are always legal
278 [\'\"]?([^\;\ \'\"\t]*)[\'\"]?[\t\;\ ]
279 # the syntax for 'use' allows version requirements
280 \s*([.0-9]*)
281 /x)
282 ) {
283 pb_log(2, "REQUIRE FOUND ***\n");
284 my ($whitespace, $statement, $module, $version) = ($1, $2, $3,$4);
285 my $usebase;
[2183]286
[3386]287 # we only consider require statements that are flush against
288 # the left edge. any other require statements give too many
289 # false positives, as they are usually inside of an if statement
290 # as a fallback module or a rarely used option
[2183]291
[3386]292 ($whitespace !~ /^\s*$/ && $statement eq "require") && next;
293
294 pb_log(2, "REQUIRE AV *** $whitespace, $statement, $module, $version ***\n");
295 # if there is some interpolation of variables just skip this
296 # dependency, we do not want
297 # do "$ENV{LOGDIR}/$rcfile";
298
299 ($module =~ m/\$/) && next;
300
301 # skip if the phrase was "use of" -- shows up in gimp-perl, et al
302 next if $module eq 'of';
303
304 # if the module ends in a comma we probaly caught some
305 # documentation of the form 'check stuff,\n do stuff, clean
306 # stuff.' there are several of these in the perl distribution
307
308 ($module =~ m/[,>]$/) && next;
309
310 # if the module name starts in a dot it is not a module name.
311 # Is this necessary? Please give me an example if you turn this
312 # back on.
313
314 # ($module =~ m/^\./) && next;
315
316 # if the module ends with .pm strip it to leave only basename.
317 # starts with /, which means its an absolute path to a file
318 if ($module =~ m(^/)) {
319 print "$module\n";
320 next;
321 }
322
323 # as seen in some perl scripts
324 # use base qw(App::CLI Class::Accessor::Chained::Fast App::CLI::Command);
325 if ($module eq 'base') {
326 $list_req{$module} = $version;
327 $line{$module} = $current_line;
328 ($module = $line) =~ s/use\s*base\s*//;
329 $module =~ s/qw\((.*)\)\s*;/$1/;
330 $module =~ s/qw(.)(.*)\1\s*;/$2/;
331 $module =~ s/\s*;$//;
332 $module =~ s/#.*//;
333 $usebase = 1;
334 }
335 # sometimes people do use POSIX qw(foo), or use POSIX(qw(foo)) etc
336 # we can strip qw.*$, as well as (.*$:
337 $module =~ s/qw.*$//;
338 $module =~ s/\(.*$//;
339
340 $module =~ s/\.pm$//;
341
342 # some perl programmers write 'require URI/URL;' when
343 # they mean 'require URI::URL;'
344
345 $module =~ s/\//::/;
346
347 # trim off trailing parenthesis if any. Sometimes people pass
348 # the module an empty list.
[3143]349
[3386]350 $module =~ s/\(\s*\)$//;
[2183]351
[3386]352 # if module is a number then both require and use interpret that
353 # to mean that a particular version of perl is specified. Don't
354 # add a dependency, though, since the rpm will already require
355 # perl-base at the build version (via find-requires)
356 next if $module =~ /^v?\d/;
[2183]357
[3386]358 # ph files do not use the package name inside the file.
359 # perlmodlib documentation says:
360 # the .ph files made by h2ph will probably end up as
361 # extension modules made by h2xs.
362 # so do not spend much effort on these.
[2183]363
[3386]364 # there is no easy way to find out if a file named systeminfo.ph
365 # will be included with the name sys/systeminfo.ph so only use the
366 # basename of *.ph files
[2183]367
[3386]368 ($module =~ m/\.ph$/) && next;
[2183]369
[3386]370 # if the module was loaded trough base, we need to split the list
371 if ($usebase) {
372 my $current_line = $line;
373 foreach my $l (split(/\s+/, $module)) {
374 next unless $l;
375 $list_req{$l} = $version;
376 $line{$l} = $current_line;
[3143]377 }
[3386]378 } else {
379 $list_req{$module}=$version;
380 $line{$module}=$current_line;
[3143]381 }
[3386]382 pb_log(2, "REQUIRE FIN *** ".Dumper(\%list_req)." ***\n");
[2183]383 }
384}
[3143]385
[3386]386close($fh) || die("$0: Could not close file: '$file' : $!\n");
387
388return(\%list_req);
[3143]389}
Note: See TracBrowser for help on using the repository browser.