File Coverage

blib/lib/Dist/Zilla/Plugin/PkgVersion.pm
Criterion Covered Total %
statement 102 105 97.1
branch 58 68 85.2
condition 6 9 66.6
subroutine 9 9 100.0
pod 0 4 0.0
total 175 195 89.7


line stmt bran cond sub pod time code
1             # ABSTRACT: add a $VERSION to your packages
2              
3             use Moose;
4 10     10   7398 with(
  10         28  
  10         93  
5             'Dist::Zilla::Role::FileMunger',
6             'Dist::Zilla::Role::FileFinderUser' => {
7             default_finders => [ ':InstallModules', ':ExecFiles' ],
8             },
9             'Dist::Zilla::Role::PPI',
10             );
11              
12             use Dist::Zilla::Pragmas;
13 10     10   69281  
  10         28  
  10         99  
14             use namespace::autoclean;
15 10     10   75  
  10         23  
  10         95  
16             #pod =head1 SYNOPSIS
17             #pod
18             #pod in dist.ini
19             #pod
20             #pod [PkgVersion]
21             #pod
22             #pod =head1 DESCRIPTION
23             #pod
24             #pod This plugin will add lines like the following to each package in each Perl
25             #pod module or program (more or less) within the distribution:
26             #pod
27             #pod $MyModule::VERSION = '0.001';
28             #pod
29             #pod or
30             #pod
31             #pod { our $VERSION = '0.001'; }
32             #pod
33             #pod ...where 0.001 is the version of the dist, and MyModule is the name of the
34             #pod package being given a version. (In other words, it always uses fully-qualified
35             #pod names to assign versions.)
36             #pod
37             #pod It will skip any package declaration that includes a newline between the
38             #pod C<package> keyword and the package name, like:
39             #pod
40             #pod package
41             #pod Foo::Bar;
42             #pod
43             #pod This sort of declaration is also ignored by the CPAN toolchain, and is
44             #pod typically used when doing monkey patching or other tricky things.
45             #pod
46             #pod =attr die_on_existing_version
47             #pod
48             #pod If true, then when PkgVersion sees an existing C<$VERSION> assignment, it will
49             #pod throw an exception rather than skip the file. This attribute defaults to
50             #pod false.
51             #pod
52             #pod =attr die_on_line_insertion
53             #pod
54             #pod By default, PkgVersion looks for a blank line after each C<package> statement.
55             #pod If it finds one, it inserts the C<$VERSION> assignment on that line. If it
56             #pod doesn't, it will insert a new line, which means the shipped copy of the module
57             #pod will have different line numbers (off by one) than the source. If
58             #pod C<die_on_line_insertion> is true, PkgVersion will raise an exception rather
59             #pod than insert a new line.
60             #pod
61             #pod =attr use_package
62             #pod
63             #pod This option, if true, will not insert an assignment to C<$VERSION> but will
64             #pod replace the existing C<package> declaration with one that includes a version
65             #pod like:
66             #pod
67             #pod package Module::Name 0.001;
68             #pod
69             #pod =attr use_our
70             #pod
71             #pod The idea here was to insert C<< { our $VERSION = '0.001'; } >> instead of C<<
72             #pod $Module::Name::VERSION = '0.001'; >>. It turns out that this causes problems
73             #pod with some analyzers. Use of this feature is deprecated.
74             #pod
75             #pod Something else will replace it in the future.
76             #pod
77             #pod =attr use_begin
78             #pod
79             #pod If true, the version assignment is wrapped in a BEGIN block. This may help in
80             #pod rare cases, such as when DynaLoader has to be called at BEGIN time, and
81             #pod requires VERSION. This option should be needed rarely.
82             #pod
83             #pod Also note that assigning to C<$VERSION> before the module has finished
84             #pod compiling can lead to confused behavior with attempts to determine whether a
85             #pod module was successfully loaded on perl v5.8.
86             #pod
87             #pod =attr finder
88             #pod
89             #pod =for stopwords FileFinder
90             #pod
91             #pod This is the name of a L<FileFinder|Dist::Zilla::Role::FileFinder> for finding
92             #pod modules to edit. The default value is C<:InstallModules> and C<:ExecFiles>;
93             #pod this option can be used more than once.
94             #pod
95             #pod Other predefined finders are listed in
96             #pod L<Dist::Zilla::Role::FileFinderUser/default_finders>.
97             #pod You can define your own with the
98             #pod L<[FileFinder::ByName]|Dist::Zilla::Plugin::FileFinder::ByName> and
99             #pod L<[FileFinder::Filter]|Dist::Zilla::Plugin::FileFinder::Filter> plugins.
100             #pod
101             #pod =cut
102              
103             my ($self) = @_;
104             $self->log("use_our option to PkgVersion is deprecated and will be removed")
105 21     21 0 74 if $self->use_our;
106 21 100       729  
107             if ($self->use_package && ($self->use_our || $self->use_begin)) {
108             $self->log_fatal("use_package and (use_our or use_begin) are not compatible");
109 21 50 33     1825 }
      66        
110 0         0 }
111              
112             my ($self) = @_;
113              
114             $self->munge_file($_) for @{ $self->found_files };
115 21     21 0 408 }
116              
117 21         49 my ($self, $file) = @_;
  21         145  
118              
119             if ($file->is_bytes) {
120             $self->log_debug($file->name . " has 'bytes' encoding, skipping...");
121 50     50 0 5067 return;
122             }
123 50 100       222  
124 1         5 if ($file->name =~ /\.pod$/) {
125 1         282 $self->log_debug($file->name . " is a pod file, skipping...");
126             return;
127             }
128 49 50       180  
129 0         0 return $self->munge_perl($file);
130 0         0 }
131              
132             has die_on_existing_version => (
133 49         229 is => 'ro',
134             isa => 'Bool',
135             default => 0,
136             );
137              
138             has die_on_line_insertion => (
139             is => 'ro',
140             isa => 'Bool',
141             default => 0,
142             );
143              
144             has use_package => (
145             is => 'ro',
146             isa => 'Bool',
147             default => 0,
148             );
149              
150             has use_our => (
151             is => 'ro',
152             isa => 'Bool',
153             default => 0,
154             );
155              
156             has use_begin => (
157             is => 'ro',
158             isa => 'Bool',
159             default => 0,
160             );
161              
162             my ($self, $package, $version) = @_;
163              
164             # the \x20 hack is here so that when we scan *this* document we don't find
165             # an assignment to version; it shouldn't be needed, but it's been annoying
166             # enough in the past that I'm keeping it here until tests are better
167 44     44   132 my $perl = $self->use_our
168             ? "our \$VERSION\x20=\x20'$version';"
169             : "\$$package\::VERSION\x20=\x20'$version';";
170              
171             return
172 44 100       1364 $self->use_begin ? "BEGIN { $perl }"
173             : $self->use_our ? "{ $perl }"
174             : $perl;
175             }
176              
177 44 100       1356 my ($self, $file) = @_;
    100          
178              
179             my $version = $self->zilla->version;
180              
181             require version;
182             Carp::croak("invalid characters in version")
183 49     49 0 143 unless version::is_lax($version);
184              
185 49         1471 my $document = $self->ppi_document_for_file($file);
186              
187 49         312 my $package_stmts = $document->find('PPI::Statement::Package');
188 49 50       192 unless ($package_stmts) {
189             $self->log_debug([ 'skipping %s: no package statement found', $file->name ]);
190             return;
191 49         1136 }
192              
193 49         17155 if ($self->document_assigns_to_variable($document, '$VERSION')) {
194 49 100       43129 if ($self->die_on_existing_version) {
195 4         31 $self->log_fatal([ 'existing assignment to $VERSION in %s', $file->name ]);
196 4         813 }
197              
198             $self->log([ 'skipping %s: assigns to $VERSION', $file->name ]);
199 45 100       224 return;
200 6 100       243 }
201 1         9  
202             my %seen_pkg;
203              
204 5         30 my $munged = 0;
205 5         1490 STATEMENT: for my $stmt (@$package_stmts) {
206             my $package = $stmt->namespace;
207             if ($seen_pkg{ $package }++) {
208 39         97 $self->log([ 'skipping package re-declaration for %s', $package ]);
209             next;
210 39         68 }
211 39         140  
212 47         188 if ($stmt->content =~ /package\s*(?:#.*)?\n\s*\Q$package/) {
213 47 100       1361 $self->log([ 'skipping private package %s in %s', $package, $file->name ]);
214 1         14 next;
215 1         327 }
216              
217             $self->log("non-ASCII package name is likely to cause problems")
218 46 100       155 if $package =~ /\P{ASCII}/;
219 2         106  
220 2         598 $self->log("non-ASCII version is likely to cause problems")
221             if $version =~ /\P{ASCII}/;
222              
223 44 50       2491 if ($self->use_package) {
224             if (my ($block) = grep {; $_->isa('PPI::Structure::Block') } $stmt->schildren) {
225             # Okay, we've encountered `package NAME BLOCK` and want to turn it into
226 44 50       206 # `package NAME VERSION BLOCK` but, to quote the PPI documentation,
227             # "we're on our own here".
228             #
229 44 100       1687 # This will also preclude us from adding "# TRIAL" because where would
230 6 100       22 # it go? Look, a block package should (in my opinion) not be the only
  20         144  
231             # or top-level package in a file, so the TRIAL comment can be
232             # elsewhere. -- rjbs, 2021-06-12
233             #
234             # First off, let's make sure we do not already have a version. If the
235             # "version" has a "{" in it, it's just the block, and we're good.
236             # Otherwise, it's going to be a real version and we need to skip.
237             if ($stmt->version !~ /\{/) {
238             $self->log([
239             "skipping package %s with version %s declared",
240             $stmt->namespace,
241             $stmt->version,
242             ]);
243 2 100       15 next STATEMENT;
244 1         41 }
245              
246             # Okay, there's a block (which we have in $block) but no version. So,
247             # we stick a Number in front of the block, then a space between them.
248             $block->insert_before( PPI::Token::Number->new($version) );
249 1         285 $block->insert_before( PPI::Token::Whitespace->new(q{ }) );
250             $munged = 1;
251             next STATEMENT;
252             }
253              
254 1         98 # Now, it's not got a block, but does it already have a version?
255 1         92 if (length $stmt->version) {
256 1         60 $self->log([
257 1         5 "skipping package %s with version %s declared",
258             $stmt->namespace,
259             $stmt->version,
260             ]);
261 4 100       15 next STATEMENT;
262 1         34 }
263              
264             # Oh, good! It's just a normal `package NAME` and we are going to add
265             # VERSION to it. This is stupid, but gets the job done.
266             my $perl = sprintf 'package %s %s;', $package, $version;
267 1         390 $perl .= ' # TRIAL' if $self->zilla->is_trial;
268              
269             my $newstmt = PPI::Token::Unknown->new($perl);
270             Carp::carp("error inserting version in " . $file->name)
271             unless $stmt->parent->__replace_child($stmt, $newstmt);
272 3         87 $munged = 1;
273 3 50       92 next STATEMENT;
274             }
275 3         17  
276 3 50       24 # the \x20 hack is here so that when we scan *this* document we don't find
277             # an assignment to version; it shouldn't be needed, but it's been annoying
278 3         129 # enough in the past that I'm keeping it here until tests are better
279 3         14 my $perl = $self->_version_assignment($package, $version);
280             $self->zilla->is_trial
281             and $perl .= ' # TRIAL';
282              
283             my $clean_version = $version =~ tr/_//dr;
284             if ($version ne $clean_version) {
285 38         173 $perl .= "\n" . $self->_version_assignment($package, $clean_version);
286 38 100       1089 }
287              
288             $self->log_debug([
289 38         183 'adding $VERSION assignment to %s in %s',
290 38 100       131 $package,
291 6         23 $file->name,
292             ]);
293              
294             my $blank;
295 38         198  
296             {
297             my $curr = $stmt;
298             while (1) {
299             # avoid bogus locations due to insert_after
300 38         1999 $document->flush_locations if $munged;
301             my $curr_line_number = $curr->line_number + 1;
302             my $find = $document->find(sub {
303 38         76 my $line = $_[1]->line_number;
  38         81  
304 38         77 return $line > $curr_line_number ? undef : $line == $curr_line_number;
305             });
306 44 100       454  
307 44         818 last unless $find and @$find == 1;
308              
309 788     788   7774 if ($find->[0]->isa('PPI::Token::Comment')) {
310 788 100       12104 $curr = $find->[0];
311 44         33379 next;
312             }
313 44 100 100     718  
314             if ("$find->[0]" =~ /\A\s*\z/) {
315 35 100       182 $blank = $find->[0];
316 6         22 }
317 6         19  
318             last;
319             }
320 29 100       107 }
321 28         321  
322             $perl = $blank ? "$perl\n" : "\n$perl";
323              
324 29         114 # Why can't I use PPI::Token::Unknown? -- rjbs, 2014-01-11
325             my $bogus_token = PPI::Token::Comment->new($perl);
326              
327             if ($blank) {
328 38 100       196 Carp::carp("error inserting version in " . $file->name)
329             unless $blank->insert_after($bogus_token);
330             $blank->delete;
331 38         197 } else {
332             my $method = $self->die_on_line_insertion ? 'log_fatal' : 'log';
333 38 100       334 $self->$method([
334 28 50       157 'no blank line for $VERSION after package %s statement in %s line %s',
335             $stmt->namespace,
336 28         2203 $file->name,
337             $stmt->line_number,
338 10 50       401 ]);
339 10         44  
340             Carp::carp("error inserting version in " . $file->name)
341             unless $stmt->insert_after($bogus_token);
342             }
343              
344             $munged = 1;
345             }
346 10 50       3308  
347             # the document is no longer correct; it must be reparsed before it can be
348             # used again, so we can't just save_ppi_document_to_file
349             # Maybe we want a way to clear the cache for the old form, though...
350 38         2241 # -- rjbs, 2016-04-24
351             $file->content($document->serialize) if $munged;
352             return;
353             }
354              
355             __PACKAGE__->meta->make_immutable;
356             1;
357 39 100       351  
358 39         257 #pod =head1 SEE ALSO
359             #pod
360             #pod Core Dist::Zilla plugins:
361             #pod L<PodVersion|Dist::Zilla::Plugin::PodVersion>,
362             #pod L<AutoVersion|Dist::Zilla::Plugin::AutoVersion>,
363             #pod L<NextRelease|Dist::Zilla::Plugin::NextRelease>.
364             #pod
365             #pod Other Dist::Zilla plugins:
366             #pod L<OurPkgVersion|Dist::Zilla::Plugin::OurPkgVersion> inserts version
367             #pod numbers using C<our $VERSION = '...';> and without changing line numbers
368             #pod
369             #pod =cut
370              
371              
372             =pod
373              
374             =encoding UTF-8
375              
376             =head1 NAME
377              
378             Dist::Zilla::Plugin::PkgVersion - add a $VERSION to your packages
379              
380             =head1 VERSION
381              
382             version 6.028
383              
384             =head1 SYNOPSIS
385              
386             in dist.ini
387              
388             [PkgVersion]
389              
390             =head1 DESCRIPTION
391              
392             This plugin will add lines like the following to each package in each Perl
393             module or program (more or less) within the distribution:
394              
395             $MyModule::VERSION = '0.001';
396              
397             or
398              
399             { our $VERSION = '0.001'; }
400              
401             ...where 0.001 is the version of the dist, and MyModule is the name of the
402             package being given a version. (In other words, it always uses fully-qualified
403             names to assign versions.)
404              
405             It will skip any package declaration that includes a newline between the
406             C<package> keyword and the package name, like:
407              
408             package
409             Foo::Bar;
410              
411             This sort of declaration is also ignored by the CPAN toolchain, and is
412             typically used when doing monkey patching or other tricky things.
413              
414             =head1 PERL VERSION
415              
416             This module should work on any version of perl still receiving updates from
417             the Perl 5 Porters. This means it should work on any version of perl released
418             in the last two to three years. (That is, if the most recently released
419             version is v5.40, then this module should work on both v5.40 and v5.38.)
420              
421             Although it may work on older versions of perl, no guarantee is made that the
422             minimum required version will not be increased. The version may be increased
423             for any reason, and there is no promise that patches will be accepted to lower
424             the minimum required perl.
425              
426             =head1 ATTRIBUTES
427              
428             =head2 die_on_existing_version
429              
430             If true, then when PkgVersion sees an existing C<$VERSION> assignment, it will
431             throw an exception rather than skip the file. This attribute defaults to
432             false.
433              
434             =head2 die_on_line_insertion
435              
436             By default, PkgVersion looks for a blank line after each C<package> statement.
437             If it finds one, it inserts the C<$VERSION> assignment on that line. If it
438             doesn't, it will insert a new line, which means the shipped copy of the module
439             will have different line numbers (off by one) than the source. If
440             C<die_on_line_insertion> is true, PkgVersion will raise an exception rather
441             than insert a new line.
442              
443             =head2 use_package
444              
445             This option, if true, will not insert an assignment to C<$VERSION> but will
446             replace the existing C<package> declaration with one that includes a version
447             like:
448              
449             package Module::Name 0.001;
450              
451             =head2 use_our
452              
453             The idea here was to insert C<< { our $VERSION = '0.001'; } >> instead of C<<
454             $Module::Name::VERSION = '0.001'; >>. It turns out that this causes problems
455             with some analyzers. Use of this feature is deprecated.
456              
457             Something else will replace it in the future.
458              
459             =head2 use_begin
460              
461             If true, the version assignment is wrapped in a BEGIN block. This may help in
462             rare cases, such as when DynaLoader has to be called at BEGIN time, and
463             requires VERSION. This option should be needed rarely.
464              
465             Also note that assigning to C<$VERSION> before the module has finished
466             compiling can lead to confused behavior with attempts to determine whether a
467             module was successfully loaded on perl v5.8.
468              
469             =head2 finder
470              
471             =for stopwords FileFinder
472              
473             This is the name of a L<FileFinder|Dist::Zilla::Role::FileFinder> for finding
474             modules to edit. The default value is C<:InstallModules> and C<:ExecFiles>;
475             this option can be used more than once.
476              
477             Other predefined finders are listed in
478             L<Dist::Zilla::Role::FileFinderUser/default_finders>.
479             You can define your own with the
480             L<[FileFinder::ByName]|Dist::Zilla::Plugin::FileFinder::ByName> and
481             L<[FileFinder::Filter]|Dist::Zilla::Plugin::FileFinder::Filter> plugins.
482              
483             =head1 SEE ALSO
484              
485             Core Dist::Zilla plugins:
486             L<PodVersion|Dist::Zilla::Plugin::PodVersion>,
487             L<AutoVersion|Dist::Zilla::Plugin::AutoVersion>,
488             L<NextRelease|Dist::Zilla::Plugin::NextRelease>.
489              
490             Other Dist::Zilla plugins:
491             L<OurPkgVersion|Dist::Zilla::Plugin::OurPkgVersion> inserts version
492             numbers using C<our $VERSION = '...';> and without changing line numbers
493              
494             =head1 AUTHOR
495              
496             Ricardo SIGNES 😏 <cpan@semiotic.systems>
497              
498             =head1 COPYRIGHT AND LICENSE
499              
500             This software is copyright (c) 2022 by Ricardo SIGNES.
501              
502             This is free software; you can redistribute it and/or modify it under
503             the same terms as the Perl 5 programming language system itself.
504              
505             =cut