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

Last change on this file since 3389 was 3389, checked in by Bruno Cornec, 9 years ago
  • Add workaround for a bug with perl 5.10.0 and scan_deps on Tie::Hash::NamedCapture (there may be others like that BTW)
  • Property svn:executable set to *
File size: 9.7 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);
16use Module::ScanDeps;
17
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
61# Globals
62my %files;
63my %found;
64my %opts;
65my @includes;
66my $req;
67GetOptions(
68 "verbose|v+" => \$opts{'v'},
69);
70
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
93if (not defined $ARGV[0]) {
94 pb_syntax();
95}
96
97pb_temp_init();
98
99my $tmpf = "$ENV{'PBTMP'}/mpgm.$$.pl";
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";
106print TMPF "# To prevent a bug with perl 5.10.0\n";
107print TMPF "require Tie::Hash::NamedCapture;\n";
108close(TMPF);
109
110push @ARGV,$tmpf;
111
112my $h = scan_deps(
113 files => \@ARGV,
114 recurse => 1,
115 );
116
117unlink("$tmpf");
118
119pb_log(1,"Returning:");
120pb_log(1,Dumper($h));
121pb_log(1,"-----------------------\n");
122foreach my $f (sort keys %$h) {
123 # Skip the temp file
124 next if ($h->{$f}->{'file'} =~ /$ENV{'PBTMP'}/);
125 print "$h->{$f}->{'file'}\n";
126}
127
128exit 0;
129
130sub mr_get_perl_modules {
131
132my $f = shift;
133my %newly_found;
134my $require;
135
136return($require) if (not defined $f);
137$require = mr_process_file($f);
138
139pb_log(1,"Requires on $f found:\n");
140pb_log(1,Dumper($require));
141pb_log(1,"---------------\n");
142
143my $cpt = keys %$require;
144
145# Exit recursion
146return($require) if ($cpt == 0);
147
148foreach my $m (keys %$require) {
149 (my $mod = $m) =~ s|::|/|g;
150 # Already found
151 next if (exists $found{$mod});
152 foreach my $d (@includes) {
153 pb_log(1,"Looking at $m in $d\n");
154 if (-f "$d/$mod.pm") {
155 $files{"$d/$mod.pm"} = $mod;
156 $found{$mod} = "$d/$mod.pm";
157 $newly_found{"$d/$mod.pm"} = $mod;
158 pb_log(1,"Found $mod in $d/$mod.pm\n");
159 last;
160 }
161 }
162 print STDERR "ERROR: Unable to find $mod. Your perl setup may be incorrect\n" if (not exists $found{$mod});
163}
164
165pb_log(1,"Files found:\n");
166pb_log(1,Dumper(\%files));
167pb_log(2,Dumper(\%found));
168pb_log(1,"---------------\n");
169
170pb_log(1,"New Files found:\n");
171pb_log(1,Dumper(\%newly_found));
172pb_log(1,"---------------\n");
173
174my $foundrec;
175
176$cpt = keys %newly_found;
177if ($cpt != 0) {
178 # Recurse on what we just found
179 foreach my $f (keys %newly_found) {
180 $foundrec = mr_get_perl_modules($f);
181 foreach my $k (keys %$foundrec) {
182 $files{$k} = $foundrec->{$k};
183 }
184 }
185
186 # Cleanup
187 foreach my $k (keys %newly_found) {
188 delete $newly_found{$k};
189 }
190}
191
192pb_log(1,"End of mr_get_perl_modules on $f we got:".Dumper(\%files)."\n");
193
194return(\%files);
195}
196
197# Adapted From /usr/lib/rpm/mageia/perl.req
198# by Ken Estes Mail.com kestes@staff.mail.com
199# used under the GPL
200
201sub mr_process_file {
202
203my $file = shift;
204my %line;
205my %list_req;
206my $current_line = undef;
207my $tag = undef;
208
209return(\%list_req) if (not defined $file);
210
211pb_log(2, "START Analyze $file\n");
212# In a recursive configuration we need a var as file handle
213open(my $fh, "$file") || return(\%list_req);
214while (my $line = <$fh>) {
215
216 # skip the "= <<" block
217
218 pb_log(2, "LINE: $line");
219 if ( ( $line =~ m/^\s*\$(.*)\s*=\s*<<\s*["'](.*)['"]/i) ||
220 ($line =~ m/^\s*\$(.*)\s*=\s*<<\s*(.*);/i) ) {
221 $tag = $2;
222 while (my $l = <$fh>) {
223 pb_log(2, "LINE-tag: $l");
224 ( $l =~ /^$tag/) && last;
225 }
226 }
227
228 pb_log(2, "PHASE 2: $line");
229 # skip the documentation
230
231 # we should not need to have item in this if statement (it
232 # properly belongs in the over/back section) but people do not
233 # read the perldoc.
234
235 if ( ($line =~ m/^=(head1|head2|pod|item)/) .. ($line =~ m/^=(cut)/) ) {
236 pb_log(2, "PHASE 2bis:$line");
237 next;
238 }
239 pb_log(2, "PHASE 3:\n");
240
241 if ( ($line =~ m/^=(over)/) .. ($line =~ m/^=(back)/) ) {
242 next;
243 }
244 pb_log(2, "PHASE 4:\n");
245
246 # skip the data section
247 if ($line =~ m/^__(DATA|END)__$/) {
248 last;
249 }
250 pb_log(2, "PHASE 5:\n");
251
252 # Each keyword can appear multiple times. Don't
253 # bother with datastructures to store these strings,
254 # if we need to print it print it now.
255
256 if ($line =~ m/^\s*\$RPM_Requires\s*=\s*["'](.*)['"]/i) {
257 foreach my $l (split(/\s+/, $1)) {
258 print "$l\n";
259 }
260 }
261 pb_log(2, "PHASE 6:\n");
262
263 if (
264
265# ouch could be in a eval, perhaps we do not want these since we catch
266# an exception they must not be required
267
268# eval { require Term::ReadLine } or die $@;
269# eval "require Term::Rendezvous;" or die $@;
270# eval { require Carp } if defined $^S; # If error/warning during compilation,
271
272
273 ($line =~ m/^(\s*) # we hope the inclusion starts the line
274 (require|use)\s+(?!\{) # do not want 'do bracket' loops
275 # quotes around name are always legal
276 [\'\"]?([^\;\ \'\"\t]*)[\'\"]?[\t\;\ ]
277 # the syntax for 'use' allows version requirements
278 \s*([.0-9]*)
279 /x)
280 ) {
281 pb_log(2, "REQUIRE FOUND ***\n");
282 my ($whitespace, $statement, $module, $version) = ($1, $2, $3,$4);
283 my $usebase;
284
285 # we only consider require statements that are flush against
286 # the left edge. any other require statements give too many
287 # false positives, as they are usually inside of an if statement
288 # as a fallback module or a rarely used option
289
290 ($whitespace !~ /^\s*$/ && $statement eq "require") && next;
291
292 pb_log(2, "REQUIRE AV *** $whitespace, $statement, $module, $version ***\n");
293 # if there is some interpolation of variables just skip this
294 # dependency, we do not want
295 # do "$ENV{LOGDIR}/$rcfile";
296
297 ($module =~ m/\$/) && next;
298
299 # skip if the phrase was "use of" -- shows up in gimp-perl, et al
300 next if $module eq 'of';
301
302 # if the module ends in a comma we probaly caught some
303 # documentation of the form 'check stuff,\n do stuff, clean
304 # stuff.' there are several of these in the perl distribution
305
306 ($module =~ m/[,>]$/) && next;
307
308 # if the module name starts in a dot it is not a module name.
309 # Is this necessary? Please give me an example if you turn this
310 # back on.
311
312 # ($module =~ m/^\./) && next;
313
314 # if the module ends with .pm strip it to leave only basename.
315 # starts with /, which means its an absolute path to a file
316 if ($module =~ m(^/)) {
317 print "$module\n";
318 next;
319 }
320
321 # as seen in some perl scripts
322 # use base qw(App::CLI Class::Accessor::Chained::Fast App::CLI::Command);
323 if ($module eq 'base') {
324 $list_req{$module} = $version;
325 $line{$module} = $current_line;
326 ($module = $line) =~ s/use\s*base\s*//;
327 $module =~ s/qw\((.*)\)\s*;/$1/;
328 $module =~ s/qw(.)(.*)\1\s*;/$2/;
329 $module =~ s/\s*;$//;
330 $module =~ s/#.*//;
331 $usebase = 1;
332 }
333 # sometimes people do use POSIX qw(foo), or use POSIX(qw(foo)) etc
334 # we can strip qw.*$, as well as (.*$:
335 $module =~ s/qw.*$//;
336 $module =~ s/\(.*$//;
337
338 $module =~ s/\.pm$//;
339
340 # some perl programmers write 'require URI/URL;' when
341 # they mean 'require URI::URL;'
342
343 $module =~ s/\//::/;
344
345 # trim off trailing parenthesis if any. Sometimes people pass
346 # the module an empty list.
347
348 $module =~ s/\(\s*\)$//;
349
350 # if module is a number then both require and use interpret that
351 # to mean that a particular version of perl is specified. Don't
352 # add a dependency, though, since the rpm will already require
353 # perl-base at the build version (via find-requires)
354 next if $module =~ /^v?\d/;
355
356 # ph files do not use the package name inside the file.
357 # perlmodlib documentation says:
358 # the .ph files made by h2ph will probably end up as
359 # extension modules made by h2xs.
360 # so do not spend much effort on these.
361
362 # there is no easy way to find out if a file named systeminfo.ph
363 # will be included with the name sys/systeminfo.ph so only use the
364 # basename of *.ph files
365
366 ($module =~ m/\.ph$/) && next;
367
368 # if the module was loaded trough base, we need to split the list
369 if ($usebase) {
370 my $current_line = $line;
371 foreach my $l (split(/\s+/, $module)) {
372 next unless $l;
373 $list_req{$l} = $version;
374 $line{$l} = $current_line;
375 }
376 } else {
377 $list_req{$module}=$version;
378 $line{$module}=$current_line;
379 }
380 pb_log(2, "REQUIRE FIN *** ".Dumper(\%list_req)." ***\n");
381 }
382}
383
384close($fh) || die("$0: Could not close file: '$file' : $!\n");
385
386return(\%list_req);
387}
Note: See TracBrowser for help on using the repository browser.