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

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