5 find_mod_deps.pl - inspect B<only hard-coded> dependencies of sets of perl files
9 Inspects the hard-coded dependencies of a set of perl files and prints
10 a summary of which modules they use (by default not including
11 inter-dependencies between the modules being inspected).
15 find_mod_deps.pl [options] [ path ... ]
17 If given any paths, inspects only the files in those paths. Defaults
18 to inspecting all perl files in the current directory.
26 If set, also print internal dependencies, i.e. the inter-dependencies
27 between the files we are inspecting.
33 Robert Buels, rbuels@cpan.org
43 use List
::MoreUtils qw
/ first_value all /;
48 use Data
::Dump
'dump';
52 getopts
('i', \
%opt) or pod2usage
();
54 -d
'./lib' or -d
'./bin' or -d
'./scripts' or die "run this script from the root dir of a distribution\n";
58 @paths = qw( t lib scripts bin cgi-bin Bio )
61 # expand any dirs into the perl files they contain
62 my @perl_files = map {
65 find
( sub { push @f, $File::Find
::name
if is_perl_file
($_) },
70 if( is_perl_file
($_) ) {
73 warn "WARNING: skipping user-specified file $_, since it is not a perl file.\n";
79 my %perl_files = map { $_ => 1 } @perl_files;
82 my $merger = Hash
::Merge
->new('RETAINMENT_PRECEDENT');
83 for my $file ( @perl_files ) {
84 my $deps = find_deps
( $file );
85 %deps = %{ $merger->merge( \
%deps, $deps ) };
90 for my $modname ( keys %deps ) {
91 if( all
{ m
|^(./)?t/| } @
{$deps{$modname}} ) {
92 $classified{build_requires
}{$modname} = $deps{$modname};
95 $classified{requires
}{$modname} = $deps{$modname};
99 print dump \
%classified;
104 my $modfile = "$modname.pm";
105 $modfile =~ s
|::|/|g
;
111 sub namespace_parent
{
113 $modname =~ s/(?:::)?[^:]+$//;
121 { open my $p, '<', $file or die "$! reading $file\n";
124 my $strip = Pod
::Strip
->new;
125 $strip->output_string(\
$nopod);
126 $strip->parse_string_document( $code );
128 my $f = IO
::String
->new( \
$nopod );
131 while( my $depline = <$f> ) {
132 $depline =~ s/#.+//; #remove comments
133 next unless $depline =~ /^\s*(use|require|extends|with)\s+.+;/;
134 next unless $depline && $depline =~ /\S/;
136 my @toks = $depline =~ /([\w:]{3,})/ig
137 or die 'cannot parse: '.$depline;
139 #warn " adding to $k->{name}\n";
142 if ( $toks[0] eq 'base' ) {
144 shift @toks if $toks[0] eq 'qw';
151 foreach my $modname (@toks) {
154 #warn "'$depline' goes to $modname\n";
156 #skip if the module is in the distribution
157 my $modfile = modfile
($modname);
158 next if !$opt{i
} && $modfile && -f
$modfile;
160 #skip if the module is in core before 5.6
161 my $rl = Module
::CoreList
->first_release($modname);
162 next if $rl && $rl <= 5.006;
164 #skip if the module is actually defined in a parent file
166 while( $p = namespace_parent
($p) ) {
167 my $p_modfile = modfile
($p);
168 #warn "checking $p / $p_modfile\n";
170 next unless $p_modfile && -f
$p_modfile;
172 open my $p, '<', $p_modfile or die "$! opening $p_modfile\n";
174 next MODNAME
if /^\s*package\s+$p\b/;
178 push @
{$deps{$modname} ||= []}, $file;
187 return -f
&& ( -x
|| /\.(pm|t|pl)$/ );