File Coverage

blib/lib/Dist/Zilla/Plugin/Conflicts.pm
Criterion Covered Total %
statement 79 91 86.8
branch 7 12 58.3
condition n/a
subroutine 19 20 95.0
pod 0 4 0.0
total 105 127 82.6


line stmt bran cond sub pod time code
1             package Dist::Zilla::Plugin::Conflicts;
2              
3 1     1   2153857 use strict;
  1         3  
  1         35  
4 1     1   5 use warnings;
  1         1  
  1         32  
5 1     1   3 use namespace::autoclean;
  1         1  
  1         11  
6              
7             our $VERSION = '0.19';
8              
9 1     1   586 use Dist::CheckConflicts 0.02 ();
  1         1377  
  1         23  
10 1     1   5 use Dist::Zilla 4.0 ();
  1         18  
  1         15  
11 1     1   4 use Dist::Zilla::File::InMemory;
  1         1  
  1         18  
12 1     1   472 use Dist::Zilla::File::FromCode;
  1         47899  
  1         41  
13              
14 1     1   8 use Moose;
  1         2  
  1         7  
15              
16             with qw(
17             Dist::Zilla::Role::FileGatherer
18             Dist::Zilla::Role::InstallTool
19             Dist::Zilla::Role::MetaProvider
20             Dist::Zilla::Role::PrereqSource
21             Dist::Zilla::Role::TextTemplate
22             );
23              
24             has _conflicts => (
25             is => 'ro',
26             isa => 'HashRef',
27             default => sub { {} },
28             );
29              
30             has _script => (
31             is => 'ro',
32             isa => 'Str',
33             predicate => '_has_script',
34             );
35              
36             has _conflicts_module_name => (
37             is => 'ro',
38             isa => 'Str',
39             init_arg => undef,
40             lazy => 1,
41             builder => '_build_conflicts_module_name',
42             );
43              
44             has _conflicts_module_path => (
45             is => 'ro',
46             isa => 'Str',
47             init_arg => undef,
48             lazy => 1,
49             builder => '_build_conflicts_module_path',
50             );
51              
52             around BUILDARGS => sub {
53             my $orig = shift;
54             my $class = shift;
55              
56             my $args = $class->$orig(@_);
57              
58             my $zilla = delete $args->{zilla};
59             my $name = delete $args->{plugin_name};
60             my $bin = delete $args->{'-script'};
61              
62             return {
63             zilla => $zilla,
64             plugin_name => $name,
65             ( defined $bin ? ( _script => $bin ) : () ),
66             _conflicts => $args,
67             };
68             };
69              
70             sub _build_conflicts_module_name {
71 1     1   1 my $self = shift;
72              
73 1         22 ( my $base = $self->zilla()->name() ) =~ s/-/::/g;
74              
75 1         79 return $base . '::Conflicts';
76             }
77              
78             sub _build_conflicts_module_path {
79 1     1   3 my $self = shift;
80              
81 1         28 my $path = join '/', split /-/, $self->zilla()->name();
82              
83 1         59 return "lib/$path/Conflicts.pm";
84             }
85              
86             sub register_prereqs {
87 1     1 0 2970 my ($self) = @_;
88              
89 1         28 $self->zilla->register_prereqs(
90             { phase => 'configure' },
91             'Dist::CheckConflicts' => '0.02',
92             );
93              
94 1         277 $self->zilla->register_prereqs(
95             { phase => 'runtime' },
96             'Dist::CheckConflicts' => '0.02',
97             );
98             }
99              
100             sub gather_files {
101 1     1 0 41271 my $self = shift;
102              
103             $self->add_file(
104             Dist::Zilla::File::FromCode->new(
105             name => $self->_conflicts_module_path(),
106 1     1   4751 code => sub { $self->_generate_conflicts_module },
107             )
108 1         48 );
109              
110 1 50       644 if ( $self->_has_script() ) {
111 1         28 $self->add_file(
112             Dist::Zilla::File::InMemory->new(
113             name => $self->_script(),
114             content => $self->_generate_conflicts_script(),
115             )
116             );
117             }
118              
119 1         2001 return;
120             }
121              
122             {
123             my $conflicts_module_template = <<'EOF';
124             package # hide from PAUSE
125             {{ $module_name }};
126              
127             use strict;
128             use warnings;
129              
130             # this module was generated with {{ ref($plugin) . ' ' . ($plugin->VERSION || '<self>') }}
131              
132             use Dist::CheckConflicts
133             -dist => '{{ $dist_name }}',
134             -conflicts => {
135             {{ $conflicts_dump }},
136             },
137             {{ $also_dump }}
138             ;
139              
140             1;
141              
142             EOF
143              
144             # Avoid false positives in toolchain things -- e.g. [SurgicalPodWeaver],
145             # and MetaCPAN seems to look for this when picking a summary for the
146             # recent uploads page.
147             $conflicts_module_template
148             .= '# ABST'
149             . 'RACT: Provide information on conflicts for {{ $dist_name }}' . "\n"
150             . '# Dist::Zilla: -'
151             . 'PodWeaver' . "\n";
152              
153             sub _generate_conflicts_module {
154 1     1   2 my $self = shift;
155              
156 1         29 my $conflicts = $self->_conflicts();
157              
158             my $conflicts_dump = join ",\n ",
159 1         2 map {qq['$_' => '$conflicts->{$_}']} sort keys %{$conflicts};
  1         6  
  1         5  
160              
161 2         14 my $also_dump = join "\n ", sort grep { $_ ne 'perl' }
162 1         24 map { $_->required_modules() }
  1         104  
163             $self->zilla()->prereqs()->requirements_for(qw(runtime requires));
164              
165 1 50       8 $also_dump
166             = ' -also => [ qw(' . "\n"
167             . q{ }
168             . $also_dump . "\n"
169             . ' ) ],' . "\n"
170             if length $also_dump;
171              
172 1         24 ( my $dist_name = $self->zilla()->name() ) =~ s/-/::/g;
173              
174 1         61 return $self->fill_in_string(
175             $conflicts_module_template,
176             {
177             plugin => \$self,
178             dist_name => \$dist_name,
179             module_name => \( $self->_conflicts_module_name() ),
180             conflicts_dump => \$conflicts_dump,
181             also_dump => \$also_dump,
182             },
183             );
184             }
185             }
186              
187             {
188             # If dzil sees this string PODXXXX anywhere in this code it uses that as the
189             # name for the module.
190             my $podname_hack = 'POD' . 'NAME';
191             my $script_template = <<'EOF';
192             #!/usr/bin/perl
193              
194             use strict;
195             use warnings;
196             # %s: {{ $filename }}
197              
198             # this script was generated with {{ ref($plugin) . ' ' . ($plugin->VERSION || '<self>') }}
199              
200             use Getopt::Long;
201             use {{ $module_name }};
202              
203             my $verbose;
204             GetOptions( 'verbose|v' => \$verbose );
205              
206             if ($verbose) {
207             {{ $module_name }}->check_conflicts;
208             }
209             else {
210             my @conflicts = {{ $module_name }}->calculate_conflicts;
211             print "$_\n" for map { $_->{package} } @conflicts;
212             exit @conflicts;
213             }
214             EOF
215             $script_template = sprintf( $script_template, $podname_hack );
216              
217             sub _generate_conflicts_script {
218 1     1   2 my $self = shift;
219              
220 1         25 ( my $filename = $self->_script() ) =~ s{^.*/}{};
221              
222 1         35 return $self->fill_in_string(
223             $script_template,
224             {
225             plugin => \$self,
226             filename => \$filename,
227             module_name => \( $self->_conflicts_module_name() ),
228             },
229             );
230             }
231             }
232              
233             # XXX - this should really be a separate phase that runs after InstallTool -
234             # until then, all we can do is die if we are run too soon
235             sub setup_installer {
236 1     1 0 14284 my $self = shift;
237              
238 1         2 my $found_installer;
239 1         2 for my $file ( @{ $self->zilla()->files() } ) {
  1         25  
240 5 100       227 if ( $file->name() =~ /Makefile\.PL$/ ) {
    50          
241 1         34 $self->_munge_makefile_pl($file);
242 1         3 $found_installer++;
243             }
244             elsif ( $file->name() =~ /Build\.PL$/ ) {
245 0         0 $self->_munge_build_pl($file);
246 0         0 $found_installer++;
247             }
248             }
249              
250 1 50       93 return if $found_installer;
251              
252 0         0 $self->log_fatal( 'No Makefile.PL or Build.PL was found.'
253             . ' [Conflicts] should appear in your dist.ini'
254             . ' after [MakeMaker] or [ModuleBuild]!' );
255             }
256              
257             sub _munge_makefile_pl {
258 1     1   3 my $self = shift;
259 1         2 my $makefile = shift;
260              
261 1         4 my $content = $makefile->content();
262              
263 1         60 $content =~ s/(use ExtUtils::MakeMaker.*)/$1\ncheck_conflicts();/;
264 1         5 $content .= "\n" . $self->_check_conflicts_sub();
265              
266 1         794 $makefile->content($content);
267              
268 1         181 return;
269             }
270              
271             sub _munge_build_pl {
272 0     0   0 my $self = shift;
273 0         0 my $build = shift;
274              
275 0         0 my $content = $build->content();
276              
277 0         0 $content =~ s/(use Module::Build.*)/$1\ncheck_conflicts();/;
278 0         0 $content .= "\n" . $self->_check_conflicts_sub();
279              
280 0         0 $build->content($content);
281              
282 0         0 return;
283             }
284              
285             {
286             my $check_conflicts_template = <<'CC_SUB';
287             sub check_conflicts {
288             if ( eval { require './{{ $conflicts_module_path }}'; 1; } ) {
289             if ( eval { {{ $conflicts_module_name }}->check_conflicts; 1 } ) {
290             return;
291             }
292             else {
293             my $err = $@;
294             $err =~ s/^/ /mg;
295             warn "***\n$err***\n";
296             }
297             }
298             else {
299             print <<'EOF';
300             ***
301             {{ $warning }}
302             ***
303             EOF
304             }
305              
306             return if $ENV{AUTOMATED_TESTING} || $ENV{NONINTERACTIVE_TESTING};
307              
308             # More or less copied from Module::Build
309             return if $ENV{PERL_MM_USE_DEFAULT};
310             return unless -t STDIN && ( -t STDOUT || !( -f STDOUT || -c STDOUT ) );
311              
312             sleep 4;
313             }
314             CC_SUB
315              
316             sub _check_conflicts_sub {
317 1     1   2 my $self = shift;
318              
319 1         2 my $warning;
320 1 50       31 if ( $self->_has_script() ) {
321 1         28 ( my $filename = $self->_script() ) =~ s{^.*/}{};
322 1         5 $warning = <<"EOF";
323             Your toolchain doesn't support configure_requires, so Dist::CheckConflicts
324             hasn't been installed yet. You should check for conflicting modules
325             manually using the '$filename' script that is installed with
326             this distribution once the installation finishes.
327             EOF
328             }
329             else {
330 0         0 my $mod = $self->_conflicts_module_name();
331 0         0 $warning = <<"EOF";
332             Your toolchain doesn't support configure_requires, so Dist::CheckConflicts
333             hasn't been installed yet. You should check for conflicting modules
334             manually by examining the list of conflicts in $mod once the installation
335             finishes.
336             EOF
337             }
338              
339 1         4 chomp $warning;
340              
341 1         31 return $self->fill_in_string(
342             $check_conflicts_template,
343             {
344             conflicts_module_path => \( $self->_conflicts_module_path() ),
345             conflicts_module_name => \( $self->_conflicts_module_name() ),
346             warning => \$warning,
347             },
348             );
349             }
350             }
351              
352             sub metadata {
353 1     1 0 16473 my $self = shift;
354              
355 1         38 my $conflicts = $self->_conflicts;
356             return { x_breaks =>
357 1         5 { map { $_ => '<= ' . $conflicts->{$_} } keys %$conflicts } };
  1         9  
358             }
359              
360             __PACKAGE__->meta->make_immutable;
361              
362             1;
363              
364             # ABSTRACT: Declare conflicts for your distro
365              
366             __END__
367              
368             =pod
369              
370             =encoding UTF-8
371              
372             =head1 NAME
373              
374             Dist::Zilla::Plugin::Conflicts - Declare conflicts for your distro
375              
376             =head1 VERSION
377              
378             version 0.19
379              
380             =head1 SYNOPSIS
381              
382             In your F<dist.ini>:
383              
384             [Conflicts]
385             Foo::Bar = 0.05
386             Thing = 2
387              
388             =head1 DESCRIPTION
389              
390             This module lets you declare conflicts on other modules (usually dependencies
391             of your module) in your F<dist.ini>.
392              
393             Declaring conflicts does several thing to your distro.
394              
395             First, it generates a module named something like
396             C<Your::Distro::Conflicts>. This module will use L<Dist::CheckConflicts> to
397             declare and check conflicts. The package name will be obscured from PAUSE by
398             putting a newline after the C<package> keyword.
399              
400             All of your runtime prereqs will be passed in the C<-also> parameter to
401             L<Dist::CheckConflicts>.
402              
403             Second, it adds code to your F<Makefile.PL> or F<Build.PL> to load the
404             generated module and print warnings if conflicts are detected.
405              
406             Finally, it adds the conflicts to the F<META.json> and/or F<META.yml> files
407             under the "x_breaks" key.
408              
409             =for Pod::Coverage gather_files
410             metadata
411             register_prereqs
412             setup_installer
413              
414             =head1 USAGE
415              
416             Using this module is simple, add a "[Conflicts]" section and list each module
417             you conflict with:
418              
419             [Conflicts]
420             Module::X = 0.02
421              
422             The version listed is the last version that I<doesn't> work. In other words,
423             any version of C<Module::X> greater than 0.02 should work with this release.
424              
425             The special key C<-script> can also be set, and given the name of a script to
426             generate, as in:
427              
428             [Conflicts]
429             -script = bin/foo-conflicts
430             Module::X = 0.02
431              
432             This script will be installed with your module, and can be run to check for
433             currently installed modules which conflict with your module. This allows users
434             an easy way to fix their conflicts - simply run a command such as
435             C<foo-conflicts | cpanm> to bring all of your conflicting modules up to date.
436              
437             B<Note:> Currently, this plugin only works properly if it is listed in your
438             F<dist.ini> I<after> the plugin which generates your F<Makefile.PL> or
439             F<Build.PL>. This is a limitation of L<Dist::Zilla> that will hopefully be
440             addressed in a future release.
441              
442             =head1 SEE ALSO
443              
444             =over 4
445              
446             =item *
447              
448             L<Dist::CheckConflicts>
449              
450             =item *
451              
452             L<Dist::Zilla::Plugin::Breaks>
453              
454             =item *
455              
456             L<Dist::Zilla::Plugin::Test::CheckBreaks>
457              
458             =back
459              
460             =head1 DONATIONS
461              
462             If you'd like to thank me for the work I've done on this module, please
463             consider making a "donation" to me via PayPal. I spend a lot of free time
464             creating free software, and would appreciate any support you'd care to offer.
465              
466             Please note that B<I am not suggesting that you must do this> in order for me
467             to continue working on this particular software. I will continue to do so,
468             inasmuch as I have in the past, for as long as it interests me.
469              
470             Similarly, a donation made in this way will probably not make me work on this
471             software much more, unless I get so many donations that I can consider working
472             on free software full time, which seems unlikely at best.
473              
474             To donate, log into PayPal and send money to autarch@urth.org or use the
475             button on this page: L<http://www.urth.org/~autarch/fs-donation.html>
476              
477             =head1 SUPPORT
478              
479             Please report any bugs or feature requests to
480             C<bug-dist-zilla-plugin-conflicts@rt.cpan.org>, or through the web interface
481             at L<http://rt.cpan.org>. I will be notified, and then you'll automatically be
482             notified of progress on your bug as I make changes.
483              
484             Bugs may be submitted through L<the RT bug tracker|http://rt.cpan.org/Public/Dist/Display.html?Name=Dist-Zilla-Plugin-Conflicts>
485             (or L<bug-dist-zilla-plugin-conflicts@rt.cpan.org|mailto:bug-dist-zilla-plugin-conflicts@rt.cpan.org>).
486              
487             There is a mailing list available for users of this distribution,
488             L<mailto:http://dzil.org/#mailing-list>.
489              
490             This distribution also has an IRC channel at
491             L<C<#distzilla> on C<irc.perl.org>|irc://irc.perl.org/#distzilla>.
492              
493             I am also usually active on IRC as 'drolsky' on C<irc://irc.perl.org>.
494              
495             =head1 DONATIONS
496              
497             If you'd like to thank me for the work I've done on this module, please
498             consider making a "donation" to me via PayPal. I spend a lot of free time
499             creating free software, and would appreciate any support you'd care to offer.
500              
501             Please note that B<I am not suggesting that you must do this> in order for me
502             to continue working on this particular software. I will continue to do so,
503             inasmuch as I have in the past, for as long as it interests me.
504              
505             Similarly, a donation made in this way will probably not make me work on this
506             software much more, unless I get so many donations that I can consider working
507             on free software full time (let's all have a chuckle at that together).
508              
509             To donate, log into PayPal and send money to autarch@urth.org, or use the
510             button at L<http://www.urth.org/~autarch/fs-donation.html>.
511              
512             =head1 AUTHOR
513              
514             Dave Rolsky <autarch@urth.org>
515              
516             =head1 CONTRIBUTOR
517              
518             =for stopwords Karen Etheridge
519              
520             Karen Etheridge <ether@cpan.org>
521              
522             =head1 COPYRIGHT AND LICENSE
523              
524             This software is Copyright (c) 2016 by Dave Rolsky.
525              
526             This is free software, licensed under:
527              
528             The Artistic License 2.0 (GPL Compatible)
529              
530             =cut