File Coverage

blib/lib/App/EUMM/Upgrade.pm
Criterion Covered Total %
statement 92 137 67.1
branch 28 84 33.3
condition 3 8 37.5
subroutine 12 16 75.0
pod 0 6 0.0
total 135 251 53.7


line stmt bran cond sub pod time code
1             package App::EUMM::Upgrade;
2              
3 3     3   14107 use strict;
  3         3  
  3         68  
4 3     3   9 use warnings;
  3         4  
  3         132  
5              
6             =head1 NAME
7              
8             App::EUMM::Upgrade - Perl tool to upgrade ExtUtils::MakeMaker-based Makefile.PL
9              
10             =head1 VERSION
11              
12             Version 1.0
13              
14             =cut
15              
16             our $VERSION = '1.0';
17              
18              
19             =head1 SYNOPSIS
20              
21             eumm-upgrade is a tool to allow using new features of ExtUtils::MakeMaker without losing
22             compatibility with older versions. It adds compatibility code to Makefile.PL and
23             tries to automatically detect some properties like license, minimum Perl version required and
24             repository used.
25              
26             Just run eumm-upgrade.pl in directory with Makefile.PL. Old file will be copied to Makefile.PL.bak.
27             If you use Github, Internet connection is required.
28              
29             You need to check resulting Makefile.PL manually as transformation is done
30             with regular expressions.
31              
32             If you need to declare number of spaces in indent in Makefile.PL, use following string at start of
33             it (set 'c-basic-offset' to your value):
34              
35             # -*- mode: perl; c-basic-offset: 4; indent-tabs-mode: nil; -*-
36              
37             =head1 new EUMM features
38              
39             LICENSE - shows license on search.cpan.org
40              
41             META_MERGE - add something (like repository URL or bugtracker UTL) to META.yml. Repository and
42             bugtracker URL are used on search.cpan.org.
43              
44             MIN_PERL_VERSION - minimum version of Perl required for module work. Not used currently, but will
45             be in the future.
46              
47             CONFIGURE_REQUIRES - modules that are used in Makefile.PL and should be installed before running it.
48              
49             TEST_REQUIRES - modules that are used in tests, but are not required by module
50             itself. Useful for ppm/OS package generaton and metadata parsing tools.
51              
52             BUILD_REQUIRES - same as TEST_REQUIRES, but for building
53              
54             AUTHOR - can be arrayref to allow several authors
55              
56             =head1 AUTHOR
57              
58             Alexandr Ciornii, C<< >>
59              
60             =head1 BUGS
61              
62             Please report any bugs or feature requests to C, or through
63             the web interface at L. I will be notified, and then you'll
64             automatically be notified of progress on your bug as I make changes.
65              
66              
67             =head1 SUPPORT
68              
69             You can find documentation for this module with the perldoc command.
70              
71             perldoc App::EUMM::Upgrade
72              
73              
74             You can also look for information at:
75              
76             =over 4
77              
78             =item * RT: CPAN's request tracker
79              
80             L
81              
82             =item * AnnoCPAN: Annotated CPAN documentation
83              
84             L
85              
86             =item * CPAN Ratings
87              
88             L
89              
90             =item * Search CPAN
91              
92             L
93              
94             =back
95              
96              
97             =head1 ACKNOWLEDGEMENTS
98              
99              
100             =head1 COPYRIGHT & LICENSE
101              
102             Copyright 2009-2017 Alexandr Ciornii.
103              
104             GPL v3
105              
106             =cut
107              
108 3     3   8 use Exporter 'import';
  3         7  
  3         473  
109             our @EXPORT=qw/remove_conditional_code find_repo convert_url_to_public convert_url_to_web/;
110             sub _indent_space_number {
111 22     22   18 my $str=shift;
112 22 100       52 $str=~/^(\s+)/ or return 0;
113 17         17 my $ind=$1;
114 17         27 $ind=~s/\t/ /gs;
115 17         26 return length($ind);
116             }
117              
118       0     sub _unindent_t {
119             # my $replace
120             # die unless
121             }
122             sub _unindent {
123 6     6   15 my $space_string_to_set=shift;
124 6         7 my $text=shift;
125             #print "#'$space_string_to_set','$text'\n";
126 6         36 my @lines=split /(?<=[\x0A\x0D])/s,$text;
127 3     3   13 use List::Util qw/min/;
  3         3  
  3         4409  
128 6         7 my $minspace=min(map {_indent_space_number($_)} @lines);
  16         16  
129 6         8 my $s1=_indent_space_number($space_string_to_set);
130             #die "$s1 > $minspace" if $s1 > $minspace;
131 6 50       9 return $text if $s1==$minspace;
132             #if (grep { $_ !~ /^$space_string_to_set/ } @lines) {
133            
134             #}
135             #my $space_str
136 6         4 my $line;
137 6         7 my $i=0;
138 6         7 foreach my $l (@lines) {
139 16 50       20 next unless $l;
140 16 100       21 if ($i==0) {
141 6         8 $l =~ s/^\s+//;
142 6         4 $i++;
143 6         8 next;
144             }
145 10 50       37 unless ($l=~s/^$space_string_to_set//) {
146 0         0 die "Text (line '$l') does not start with removal line ($space_string_to_set)";
147             }
148 10 50       13 next unless $l;
149 10 50       16 if ($l=~m/^(\s+)/) {
150 10         10 my $space=$1;
151 10 100       11 if (!defined $line) {
152 2         2 $line=$space;
153 2         3 next;
154             } else {
155 8 50       21 if ($space=~/^$line/) {
    0          
156 8         8 next;
157             } elsif ($line=~/^$space/) {
158 0         0 $line=$space;
159 0 0       0 if ($line eq '') {
160             #warn("line set to '' on line '$l'");
161             }
162             } else {
163 0         0 die "Cannot find common start, on line '$l'";
164             }
165             }
166             } else {
167 0         0 return $text;
168             }
169             }
170 6 50 66     21 if (!$line and $i>1) {
171 0         0 die "Cannot find common start";
172             }
173 6         4 $i=0;
174 6         6 foreach my $l (@lines) {
175 16 50       18 next unless $l;
176 16 100       19 if ($i==0) {
177 6         9 $l="$space_string_to_set$l";
178 6         4 $i++;
179 6         6 next;
180             }
181 10 50       28 unless ($l=~s/^$line//) {
182 0         0 die "Text (line '$l') does not start with calculated removal line ($space_string_to_set)";
183             }
184 10         13 $l="$space_string_to_set$l";
185             }
186 6         21 return (join("",@lines)."");
187              
188             #foreach
189             #$text=~s/^(\s+)(\S)/_unindent_t(qq{$1},qq{$space_string_to_set}).qq{$2}/egm;
190            
191             #my $style=shift;
192             }
193              
194             sub remove_conditional_code {
195 8     8 0 1952 my $content=shift;
196 8         8 my $space=shift;
197 8         10 $content=~s/(WriteMakefile\()(?!\%)(\S)/$1\n$space$2/;
198              
199 8         10 $content=~s/
200             \(\s*\$\]\s*>=\s*5\.005\s*\?\s*(?:\#\#\s*\QAdd these new keywords supported since 5.005\E\s*)?
201             \s+\(\s*ABSTRACT(?:_FROM)?\s*=>\s*'([^'\n]+)',\s*(?:\#\s*\Qretrieve abstract from module\E\s*)?
202             \s+AUTHOR\s*=>\s*'([^'\n]+)'
203             \s*\)\s*\Q: ()\E\s*\),\s+
204             /ABSTRACT_FROM => '$1',\n${space}AUTHOR => '$2',\n/sx;
205              
206 8         25 my $eumm_version_check=qr/\$ ExtUtils::MakeMaker::VERSION\s+
207             (?:g[et]\s+' [\d\._]+ ' \s* | >=?\s*[\d\._]+\s+) |
208             eval\s*{\s*ExtUtils::MakeMaker->VERSION\([\d\._]+\)\s*}\s*
209             /xs;
210 8         131 $content=~s/
211             ^(\s*)\(\s* $eumm_version_check
212             \?\s+\(\s* #[\n\r]
213             ( [ \t]*[^()]+? ) #main text, should not contain ()
214             \s*
215             \)\s*\:\s*\(\)\s*\),
216 5         7 /_unindent($1,$2)/msxge;
217              
218 8         35 $content=~s/
219             \(\s*\$\]\s* \Q>=\E \s* 5[\d\._]+ \s* \?\s*\( \s*
220             ( [^()]+? ) ,? \s*
221             \)\s*\:\s*\(\)\s*\),
222             /$1,/sxg;
223             # ($] >= 5.005 ?
224             # (AUTHOR => 'Author ') : ()),
225 8         33 return $content;
226             }
227              
228             sub _do_replace {
229 2     2   3 my $spaces=shift;
230 2         2 my $i_from=shift;
231 2         3 my $i_to=shift;
232 2         1 my $len=length($spaces);
233 2         4 my $l1=int($len/$i_from);
234 2 50       4 if ($i_to==0) {
235 0         0 return "\t"x$l1;
236             } else {
237 2         9 return " " x ($l1*$i_to);
238             }
239             }
240              
241             sub apply_indent {
242 1     1 0 237 my $content=shift;
243 1   50     3 my $i_from=shift || die;
244 1         2 my $i_to=shift;
245 1         19 $content=~s/^((?:[ ]{$i_from})+)/_do_replace($1,$i_from,$i_to)/emg;
  2         4  
246 1         5 return $content;
247             }
248              
249             sub add_new_fields {
250 2     2 0 484 my $content = shift;
251 2         2 my $new_fields = shift;
252 2         3 my $text2replace = 'WriteMakefile\(';
253 2 100       9 if ($content =~ /WriteMakefile\(\s*(\%\w+)\s*\);/) {
254 1         2 my $var_params = $1;
255 1         14 $text2replace = qr/$var_params\s*=\s*\(\s*$/m;
256 1 50       18 $content =~ s/($text2replace)/$1$new_fields/ or die "Cannot find $var_params initialization in Makefile.PL";
257 1         5 $content =~ s/WriteMakefile\s*\(/WriteMakefile(/s;
258             } else {
259 1         7 $content =~ s/WriteMakefile\s*\(/WriteMakefile($new_fields/s;
260             }
261 2         7 return $content;
262             }
263              
264              
265              
266             #_find_repo copied from Module::Install::Repository;
267             #by Tatsuhiko Miyagawa
268             #See Module::Install::Repository for copyright
269              
270             sub _execute {
271 0     0   0 my ($command) = @_;
272 0         0 local $ENV{LC_ALL} = "C";
273 0         0 `$command`;
274             }
275              
276             sub find_repo {
277 0     0 0 0 return _find_repo(\&_execute);
278             }
279              
280             sub _find_repo {
281 0     0   0 my ($execute) = @_;
282              
283 0 0 0     0 if (-e ".git") {
    0          
    0          
    0          
    0          
284             # TODO support remote besides 'origin'?
285 0         0 my $git_url = '';
286 0 0       0 if ($execute->('git remote show -n origin') =~ /URL: (.*)$/m) {
    0          
287             # XXX Make it public clone URL, but this only works with github
288 0         0 $git_url = $1;
289 0         0 $git_url =~ s![\w\-]+\@([^:]+):!git://$1/!;
290 0         0 return ('git', $git_url);
291             } elsif ($execute->('git svn info') =~ /URL: (.*)$/m) {
292 0         0 $git_url = $1;
293             }
294 0 0       0 return '' if $git_url =~ /\A\w+\z/;# invalid github remote might come back with just the remote name
295 0         0 return ('git', $git_url);
296             } elsif (-e ".svn") {
297 0 0       0 if ($execute->('svn info') =~ /URL: (.*)$/m) {
298 0         0 return ('svn', $1);
299             }
300             } elsif (-e "_darcs") {
301             # defaultrepo is better, but that is more likely to be ssh, not http
302 0 0       0 if (my $query_repo = `darcs query repo`) {
303 0 0       0 if ($query_repo =~ m!Default Remote: (http://.+)!) {
304 0         0 return ('darcs', $1);
305             }
306             }
307              
308 0 0       0 open my $handle, '<', '_darcs/prefs/repos' or return;
309 0         0 while (<$handle>) {
310 0         0 chomp;
311 0 0       0 return ('darcs', $_) if m!^http://!;
312             }
313             } elsif (-e ".hg") {
314 0 0       0 if ($execute->('hg paths') =~ /default = (.*)$/m) {
315 0         0 my $mercurial_url = $1;
316 0         0 $mercurial_url =~ s!^ssh://hg\@(bitbucket\.org/)!https://$1/!;
317 0         0 return ('hg', $mercurial_url);
318             }
319             } elsif ($ENV{HOME} && -e "$ENV{HOME}/.svk") {
320             # Is there an explicit way to check if it's an svk checkout?
321 0 0       0 my $svk_info = `svk info` or return;
322             SVK_INFO: {
323 0 0       0 if ($svk_info =~ /Mirrored From: (.*), Rev\./) {
  0         0  
324 0         0 return ('svk', $1);
325             }
326              
327 0 0       0 if ($svk_info =~ m!Merged From: (/mirror/.*), Rev\.!) {
328 0 0       0 $svk_info = `svk info /$1` or return;
329 0         0 redo SVK_INFO;
330             }
331             }
332              
333 0         0 return;
334             }
335             }
336              
337             sub convert_url_to_public {
338 1     1 0 5 my $url = shift;
339 1 50       5 $url =~ s#^(?:ssh://|git://)?git\@(github\.com|bitbucket\.org)[:/]#https://$1/# and return $url;
340             #ssh://git@bitbucket.org/shlomif/fc-solve.git
341              
342 1 50       3 $url =~ s#^(?:ssh://)hg\@(bitbucket\.org)/#https://$1/# and return $url;
343             #ssh://hg@bitbucket.org/shlomif/app-notifier
344              
345 1 50       16 $url =~ s#^https://(\w+)\@(bitbucket\.org)/#https://$2/# and return $url;
346             #ssh://hg@bitbucket.org/shlomif/app-notifier
347              
348 0         0 return $url;
349             }
350              
351             sub convert_url_to_web {
352 5     5 0 8 my $url = shift;
353 5 50       10 return unless $url;
354 5 100       42 $url =~ s#^(?:https?|git)://(github\.com|bitbucket\.org)/(.*)\.git#https://$1/$2# and return $url;
355 2 50       30 $url =~ s#^https?://(bitbucket\.org)/(.*)#https://$1/$2# and return $url;
356 0           return;
357             }
358              
359             1; # End of App::EUMM::Upgrade