File Coverage

blib/lib/Dist/Zilla/Plugin/CheckSelfDependency.pm
Criterion Covered Total %
statement 53 53 100.0
branch 12 16 75.0
condition 6 9 66.6
subroutine 10 10 100.0
pod 0 1 0.0
total 81 89 91.0


line stmt bran cond sub pod time code
1 6     6   3937866 use strict;
  6         13  
  6         254  
2 6     6   26 use warnings;
  6         9  
  6         340  
3             package Dist::Zilla::Plugin::CheckSelfDependency; # git description: v0.009-1-ged0b9db
4             # ABSTRACT: Check if your distribution declares a dependency on itself
5             # KEYWORDS: plugin validate distribution prerequisites dependencies modules
6             # vim: set ts=8 sw=4 tw=78 et :
7             $Dist::Zilla::Plugin::CheckSelfDependency::VERSION = '0.010';
8 6     6   27 use Moose;
  6         8  
  6         52  
9             with 'Dist::Zilla::Role::AfterBuild',
10             'Dist::Zilla::Role::FileFinderUser' => {
11             default_finders => [ ':InstallModules' ],
12             },
13             ;
14 6     6   34187 use Module::Metadata 1.000023;
  6         26521  
  6         280  
15 6     6   52 use CPAN::Meta::Prereqs 2.132830; # for merged_requirements
  6         113  
  6         117  
16 6     6   26 use CPAN::Meta::Requirements;
  6         7  
  6         99  
17 6     6   25 use namespace::autoclean;
  6         8  
  6         59  
18              
19             around dump_config => sub
20             {
21             my ($orig, $self) = @_;
22             my $config = $self->$orig;
23              
24             $config->{+__PACKAGE__} = {
25             finder => $self->finder,
26             };
27              
28             return $config;
29             };
30              
31             sub after_build
32             {
33 10     10 0 957245 my $self = shift;
34              
35 6         25 my %prereqs = map { $_ => 1 }
  6         18  
36 6         23 map { keys %$_ }
37 40         6197 map { values %$_ }
38 10         367 grep { defined }
39 10         34 @{ $self->zilla->prereqs->as_string_hash }{qw(configure build runtime test)};
40              
41 10         353 my $develop_prereqs = $self->zilla->prereqs->cpan_meta_prereqs
42             ->merged_requirements(['develop'], [qw(requires recommends suggests)]);
43 10         2339 my $develop_prereqs_hash = $develop_prereqs->as_string_hash;
44              
45 10         412 my $provides = $self->zilla->distmeta->{provides}; # copy, to avoid autovivifying
46              
47 10         338 my @errors;
48             # when 'provides' data is mandatory, we will rely on what it says -
49             # but for now, we will check our modules explicitly for provided packages.
50 10         17 foreach my $file (@{$self->found_files})
  10         52  
51             {
52 10 50 33     6011 $self->log_fatal(sprintf('Could not decode %s: %s', $file->name, $file->added_by))
53             if $file->can('encoding') and $file->encoding eq 'bytes';
54              
55 10         206 my $fh;
56 6 50   6   700 ($file->can('encoding')
  6 50   6   8  
  6         47  
  6         5707  
  6         11  
  6         27  
  10         348  
57             ? open $fh, sprintf('<:encoding(%s)', $file->encoding), \$file->encoded_content
58             : open $fh, '<', \$file->content)
59             or $self->log_fatal('cannot open handle to ' . $file->name . ' content: ' . $!);
60              
61 10         6835 my @packages = Module::Metadata->new_from_handle($fh, $file->name)->packages_inside;
62 10         2994 foreach my $package (@packages)
63             {
64 10 100 100     79 if (exists $prereqs{$package}
      66        
65             or (exists $develop_prereqs_hash->{$package}
66             # you can only have a develop prereq on yourself if you
67             # use 'provides' metadata - so we're darned sure we
68             # matched up the right module names
69             and not exists $provides->{$package}))
70             {
71 5         28 push @errors, $package . ' is listed as a prereq, but is also provided by this dist ('
72             . $file->name . ')!';
73 5         269 next;
74             }
75              
76 5 100       48 next if not exists $develop_prereqs_hash->{$package};
77              
78 3 50       13 my $version = $provides ? $provides->{$package}{version} : $self->zilla->version;
79 3 100       16 if (not $develop_prereqs->accepts_module($package => $version))
80             {
81 1         59 push @errors, "$package $develop_prereqs_hash->{$package} is listed as a develop prereq, "
82             . 'but this dist doesn\'t provide that version ('
83             . $file->name . ' only has ' . $version . ')!';
84             }
85             }
86             }
87              
88 10 100       267 $self->log_fatal(@errors) if @errors;
89             }
90              
91             __PACKAGE__->meta->make_immutable;
92              
93             __END__
94              
95             =pod
96              
97             =encoding UTF-8
98              
99             =head1 NAME
100              
101             Dist::Zilla::Plugin::CheckSelfDependency - Check if your distribution declares a dependency on itself
102              
103             =head1 VERSION
104              
105             version 0.010
106              
107             =head1 SYNOPSIS
108              
109             In your F<dist.ini>:
110              
111             [CheckSelfDependency]
112              
113             =head1 DESCRIPTION
114              
115             =for Pod::Coverage after_build
116              
117             =for stopwords indexable
118              
119             This is a L<Dist::Zilla> plugin that runs in the I<after build> phase, which
120             checks all of your module prerequisites (all phases, all types except develop) to confirm
121             that none of them refer to modules that are B<provided> by this distribution
122             (that is, the metadata declares the module is indexable).
123              
124             In addition, all modules B<in> the distribution are checked against all module
125             prerequisites (all phases, all types B<including> develop). Thus, it is
126             possible to ship a L<Dist::Zilla> plugin and use (depend on) yourself, but
127             errors such as declaring a dependency on C<inc::HelperPlugin> are still caught.
128              
129             While some prereq providers (e.g. L<C<[AutoPrereqs]>|Dist::Zilla::Plugin::AutoPrereqs>)
130             do not inject dependencies found internally, there are many plugins that
131             generate code and also inject the prerequisites needed by that code, without
132             regard to whether some of those modules might be provided by your dist.
133             This problem is particularly acute when packaging low-level toolchain distributions.
134              
135             If such modules are found, the build fails. To remedy the situation, remove
136             the plugin that adds the prerequisite, or remove the prerequisite itself with
137             L<C<[RemovePrereqs]>|Dist::Zilla::Plugin::RemovePrereqs>. (Remember that
138             plugin order is significant -- you need to remove the prereq after it has been
139             added.)
140              
141             =head1 CONFIGURATION OPTIONS
142              
143             =head2 C<finder>
144              
145             =for stopwords FileFinder
146              
147             This is the name of a L<FileFinder|Dist::Zilla::Role::FileFinder> for finding
148             modules to check. The default value is C<:InstallModules>; this option can be
149             used more than once.
150              
151             Other predefined finders are listed in
152             L<Dist::Zilla::Role::FileFinderUser/default_finders>.
153             You can define your own with the
154             L<[FileFinder::ByName]|Dist::Zilla::Plugin::FileFinder::ByName> and
155             L<[FileFinder::Filter]|Dist::Zilla::Plugin::FileFinder::Filter> plugins.
156              
157             =head1 SUPPORT
158              
159             =for stopwords irc
160              
161             Bugs may be submitted through L<the RT bug tracker|https://rt.cpan.org/Public/Dist/Display.html?Name=Dist-Zilla-Plugin-CheckSelfDependency>
162             (or L<bug-Dist-Zilla-Plugin-CheckSelfDependency@rt.cpan.org|mailto:bug-Dist-Zilla-Plugin-CheckSelfDependency@rt.cpan.org>).
163             I am also usually active on irc, as 'ether' at C<irc.perl.org>.
164              
165             =head1 AUTHOR
166              
167             Karen Etheridge <ether@cpan.org>
168              
169             =head1 COPYRIGHT AND LICENSE
170              
171             This software is copyright (c) 2013 by Karen Etheridge.
172              
173             This is free software; you can redistribute it and/or modify it under
174             the same terms as the Perl 5 programming language system itself.
175              
176             =cut