App-CamelPKI
view release on metacpan or search on metacpan
t/maintainer/dependencies.t view on Meta::CPAN
of hashes of the form
{
line => $line,
file => $file,
}
pointing at the precise location in the source code where the
dependency was found. Only dependencies against a real C<.pm> file
are accounted for (not C<.so>, not C<.bs>); also,
L</@maintainer_dependencies> are not listed if found in
C<t/maintainer>.
=cut
sub list_deps {
my $retval;
foreach my $file (@_) {
die "Cannot open $file: $!" unless defined
(my $fd = IO::File->new($file, "<"));
# Here we go again with your usual half-assed Perl parser...
LINE: while(my $line = $fd->getline) {
CHUNK: foreach my $pm (Module::ScanDeps::scan_line($line),
scan_line_some_more($line, $file, $fd)) {
next LINE if skip_pod($file, $fd, $pm);
next CHUNK unless ($pm =~ m/\.pm$/);
my $module = file2mod($pm);
next CHUNK if
($file =~ m/\bt\b\W+\bmaintainer\b/ &&
grep { $module eq $_ } @maintainer_dependencies);
# Works around bug #25547 on rt.cpan.org:
next CHUNK if ($module eq "File::Glob") &&
($line !~ m/Glob/);
# Now ask Module::ScanDeps to find the actual module
# on disk. If not found (or found within our own
# distribution), then count as a false positive.
my %rv;
Module::ScanDeps::add_deps(rv => \%rv, modules => [ $pm ]);
next CHUNK unless (exists($rv{$pm}) &&
exists($rv{$pm}->{file}));
next CHUNK if is_our_own_file($rv{$pm}->{file});
push(@{$retval->{$module}},
{ file => $file,
line => $fd->input_line_number });
}
skip_here_document($file, $fd, $line);
}
}
return $retval;
}
=head2 skip_pod($filename, $fd, $pm)
=head2 skip_here_document($filename, $fd, $line)
Both functions advance $fd, an instance of L<IO::Handle>, to skip past
non-Perl source code constructs, and return true if they indeed did
skip something (or throw an exception if they tried and failed). $pm
is a token returned by L<Module::ScanDeps/scan_line>; $line is a line
of the Perl source file. $filename is only used to construct the text
of error messages.
=cut
sub skip_pod {
my ($file, $fd, $pm) = @_;
return unless $pm eq '__POD__';
my $podline = $fd->input_line_number;
while (<$fd>) { return 1 if (/^=cut/) }
die <<"MESSAGE";
Could not find end of POD at $file line $podline
MESSAGE
}
sub skip_here_document {
my ($file, $fd, $line) = @_;
# Regex mostly lifted from Emacs' cperl-mode.el, which may or may
# not be accurate. The case of multiple here-docs on the same
# line is not accounted for.
$line =~ s/#.*$//g; # Snip comments
return unless
($line =~ m/ (.*)
<< \s*
(?: '(.*?)' | "(.*?)" | ([A-Za-z][A-Za-z0-9_]*) )
/x);
my $leadingstuff = $1;
my $heredelim = $2 || $3 || $4;
# Eval'ed here-docs don't count, they are treated as real code (!):
return if ($leadingstuff =~ m/eval\s*$/);
my $hereline = $fd->input_line_number;
while (<$fd>) { return 1 if (/^\Q$heredelim\E/) }
die <<"MESSAGE";
Could not find end of here document ($heredelim) at $file line $hereline
MESSAGE
}
=head2 scan_line_some_more($line, $filename, $fd)
Works like L<Module::ScanDeps/scan_line>, and works around the
limitations thereof by detecting more forms of dependencies. $fd is
available in case the code wants to slurp more lines in order to get
hold of a complete Perl statement. $filename is only used to generate
error messages.
=cut
sub scan_line_some_more {
local $_ = shift;
my ($file, $fd) = @_;
my $lineno = $fd->input_line_number;
my @retval;
# Catalyst mojo constructs:
if (m|use \s+ Catalyst \s+ |x) {
until (m/ use \s+ Catalyst \s+ (.*);/sx) {
my $nextline = <$fd>;
die <<"MESSAGE" if ! defined $nextline;
( run in 1.807 second using v1.01-cache-2.11-cpan-39bf76dae61 )