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.
31 If set, print the dependencies in a format suitable for cutting and
32 pasting directly into a Build.PL (i.e. Module::Build)
36 If set, print the dependencies in a format suitable for cutting and
37 pasting directly into a Makefile.PL (i.e. Module::Install)
41 If set, print the dependencies in a format suitable for cutting and
42 pasting directly into a dist.ini (i.e. Dist::Zilla). Although, if
43 you're using Dist::Zilla, you probably have it configured to be
44 auto-discovering deps, and it will find the same deps as this script.
50 Robert Buels, rbuels@cpan.org
60 use List
::MoreUtils qw
/ first_value all /;
65 use Data
::Dump
'dump';
69 getopts
('iBMZ', \
%opt) or pod2usage
();
71 -d
'./lib' or -d
'./bin' or -d
'./scripts' or die "run this script from the root dir of a distribution\n";
75 @paths = qw( t lib scripts bin cgi-bin Bio )
78 # expand any dirs into the perl files they contain
79 my @perl_files = map {
82 find
( sub { push @f, $File::Find
::name
if is_perl_file
($_) },
87 if( is_perl_file
($_) ) {
90 warn "WARNING: skipping user-specified file $_, since it is not a perl file.\n";
98 my %perl_files = map { $_ => 1 } @perl_files;
101 my $merger = Hash
::Merge
->new('RETAINMENT_PRECEDENT');
102 for my $file ( @perl_files ) {
103 my $deps = find_deps
( $file );
104 %deps = %{ $merger->merge( \
%deps, $deps ) };
109 for my $modname ( keys %deps ) {
110 if( all
{ m
|^(./)?t/| } @
{$deps{$modname}} ) {
111 $classified{build_requires
}{$modname} = $deps{$modname};
114 $classified{requires
}{$modname} = $deps{$modname};
118 # decide which format to print in
120 for ( values %classified ) {
121 $_ = 0 for values %$_;
123 print dump \
%classified;
125 print "requires '$_' => 0;\n"
126 for sort { lc $a cmp lc $b } keys %{$classified{requires
}};
127 print "test_requires '$_' => 0;\n"
128 for sort { lc $a cmp lc $b } keys %{$classified{build_requires
}};
132 for sort { lc $a cmp lc $b } keys %{$classified{requires
}};
134 print "\n[Prereqs / TestRequires]\n";
136 for sort { lc $a cmp lc $b } keys %{$classified{build_requires
}};
139 print dump \
%classified;
144 ################## helpers #####################
148 my $modfile = "$modname.pm";
149 $modfile =~ s
|::|/|g
;
155 sub namespace_parent
{
157 $modname =~ s/(?:::)?[^:]+$//;
165 { open my $p, '<', $file or die "Could not read file '$file': $!\n";
168 my $strip = Pod
::Strip
->new;
169 $strip->output_string(\
$nopod);
170 $strip->parse_string_document( $code );
172 my $f = IO
::String
->new( \
$nopod );
175 while( my $depline = <$f> ) {
176 $depline =~ s/#.+//; #remove comments
177 next unless $depline =~ /^\s*(use|require|extends|with)\s+.+;/;
178 next unless $depline && $depline =~ /\S/;
180 my @toks = $depline =~ /([\w:]{3,})/ig
181 or die 'cannot parse: '.$depline;
183 #warn " adding to $k->{name}\n";
186 if ( $toks[0] eq 'base' ) {
188 shift @toks if $toks[0] eq 'qw';
195 foreach my $modname (@toks) {
198 #warn "'$depline' goes to $modname\n";
200 #skip if the module is in the distribution
201 my $modfile = modfile
($modname);
202 next if !$opt{i
} && $modfile && -f
$modfile;
204 #skip if the module is in core before 5.6
205 my $rl = Module
::CoreList
->first_release($modname);
206 next if $rl && $rl <= 5.006;
208 #skip if the module is actually defined in a parent file
210 while( $p = namespace_parent
($p) ) {
211 my $p_modfile = modfile
($p);
212 #warn "checking $p / $p_modfile\n";
214 next unless $p_modfile && -f
$p_modfile;
216 open my $p, '<', $p_modfile or die "Could not read file '$p_modfile': $!\n";
218 next MODNAME
if /^\s*package\s+$p\b/;
222 push @
{$deps{$modname} ||= []}, $file;
231 return -f
&& ( -x
|| /\.(pm|t|pl)$/ );