]> git.vpit.fr Git - perl/modules/Task-Devel-Cover-Recommended.git/blob - samples/generate.pl
Make sure no literal 'package FOO' appears in samples/generate.pl
[perl/modules/Task-Devel-Cover-Recommended.git] / samples / generate.pl
1 #!perl
2
3 use strict;
4 use warnings;
5
6 use autodie;
7 use version;
8
9 use Archive::Extract;
10 use Cwd;
11 use File::Fetch;
12 use File::Spec;
13 use File::Temp 0.19;
14 use List::Util;
15 use Parse::CPAN::Meta;
16
17 use File::HomeDir;
18 use Parse::CPAN::Packages::Fast;
19
20 my $target_dist = 'Devel-Cover';
21 my $first_year  = 2012;
22 my $cpan_mirror = 'http://www.cpan.org';
23 my %prereq_skip = (
24  'run' => {
25   'Perl::Tidy'        => 1,
26   'Test::Differences' => 1,
27  },
28 );
29 my %prereq_desc = (
30  'PPI::HTML' => 'Devel::Cover lets you optionally pick between L<PPI::HTML> and L<Perl::Tidy>, but it will only use the former if both are installed.',
31 );
32
33 sub get_latest_dist {
34  my $dist = shift;
35
36  my $home = File::HomeDir->my_home;
37  my $pkgs = File::Spec->catfile($home, qw<.cpanplus 02packages.details.txt.gz>);
38  my $pcp  = Parse::CPAN::Packages::Fast->new($pkgs);
39
40  my $d = $pcp->latest_distribution($dist);
41  die "Could not find distribution '$dist' on the CPAN" unless $d;
42
43  return $d;
44 }
45
46 sub get_dist_meta {
47  my $d = shift;
48
49  $cpan_mirror =~s{/+$}{}g;
50  my $cpanid   = $d->cpanid;
51  my ($cp, $c) = $cpanid =~ /^((.).)/;
52  my $uri      = join '/', $cpan_mirror, 'authors', 'id', $c, $cp, $cpanid,
53                           $d->filename;
54
55  my $tmp_dir     = File::Temp->newdir;
56  # Force symlinks resolution
57  my $tmp_dirname = Cwd::abs_path($tmp_dir->dirname);
58
59  my $ff      = File::Fetch->new(uri => $uri);
60  my $archive = $ff->fetch(to => $tmp_dirname);
61  die $ff->error unless $archive;
62
63  my $ae = Archive::Extract->new(archive => $archive);
64  $ae->extract(to => $tmp_dirname) or die $ae->error;
65
66  my $files = {
67   map { File::Spec->catfile($tmp_dirname, $_) => 1 }
68    @{$ae->files}
69  };
70  my $abs_extract_path = Cwd::abs_path($ae->extract_path);
71  my @meta_candidates  = map File::Spec->catfile($abs_extract_path, $_),
72                          qw<META.json META.yml>;
73  my $meta_file;
74  for my $file (@meta_candidates) {
75   if ($files->{$file}) {
76    $meta_file = $file;
77    last;
78   }
79  }
80  die 'No META file for ' . $d->distvname . "\n" unless $meta_file;
81
82  return Parse::CPAN::Meta->load_file($meta_file);
83 }
84
85 my $latest_target  = get_latest_dist($target_dist);
86 my $target_version = $latest_target->version;
87 my $meta           = get_dist_meta($latest_target);
88
89 my %eumm_phases = (
90  configure => [ qw<configure>  ],
91  build     => [ qw<build test> ],
92  run       => [ qw<runtime>    ],
93 );
94 my %prereqs = (
95  configure => {
96   'ExtUtils::MakeMaker' => '0',
97  },
98  build => {
99   'ExtUtils::MakeMaker' => '0',
100   'Test::More'          => '0',
101  },
102  perl => '5',
103 );
104
105 for my $eumm_phase (keys %eumm_phases) {
106  my $prereqs = $prereqs{$eumm_phase} ||= { };
107  my $skip    = $prereq_skip{$eumm_phase};
108
109  for my $meta_phase (@{$eumm_phases{$eumm_phase}}) {
110
111   for my $type (qw<requires recommends>) {
112    my $phase_prereqs = $meta->{prereqs}{$meta_phase}{$type};
113    next unless $phase_prereqs;
114
115    while (my ($module, $version) = each %$phase_prereqs) {
116     next if $skip->{$module};
117
118     if ($module eq 'perl') {
119      if (not $prereqs{perl} or $prereqs{perl} < $version) {
120       $prereqs{perl} = $version;
121      }
122     } elsif (not exists $prereqs->{$module} or
123              version->parse($prereqs->{$module}) < version->parse($version)) {
124      $prereqs->{$module} = $version;
125     }
126    }
127   }
128  }
129 }
130
131 (my $target_pkg = $target_dist) =~ s/-/::/g;
132 my $task_pkg    = "Task::${target_pkg}::Recommended";
133 (my $task_file  = "lib/$task_pkg.pm") =~ s{::}{/}g;
134 my $years       = join ',', $first_year .. ((gmtime)[5] + 1900);
135
136 my $old_task_version = '0.0.0';
137 if (-e $task_file) {
138  open my $old_fh, '<', $task_file;
139  while (<$old_fh>) {
140   if (/our\s*\$VERSION\s*=\s*(.*);/) {
141    $old_task_version = $1;
142    $old_task_version =~ s/^(['"])(.*)\1$/$2/;
143   }
144  }
145  close $old_fh;
146 }
147 my ($old_task_revision) = $old_task_version =~ /([0-9]+)$/;
148
149 my $new_task_version  = version->parse($target_version)->normal;
150 my $new_task_revision = $old_task_revision;
151 ++$new_task_revision
152       if version->parse($new_task_version) <= version->parse($old_task_version);
153 if (($target_version =~ tr/.//) < 2) {
154  my @components     = split /\./, $new_task_version;
155  $components[2]     = $new_task_revision;
156  $new_task_version  = join '.', @components;
157 } else {
158  $new_task_version .= ".$new_task_revision";
159 }
160
161 (my $bug_queue = $task_pkg) =~ s/::/-/g;
162 my $bug_email  = "bug-\L$bug_queue\E at rt.cpan.org";
163 $bug_queue     = "http://rt.cpan.org/NoAuth/ReportBug.html?Queue=$bug_queue";
164
165 sub deplist_to_pod {
166  my @deplist = @_;
167  return 'None.' unless @deplist;
168
169  my $pod = "=over 4\n\n";
170  while (@deplist) {
171   my ($module, $version) = splice @deplist, 0, 2;
172   my $X = $module eq 'perl' ? 'C' : 'L';
173   $pod .= "=item $X<$module>";
174   $pod .= " $version" if $version;
175   $pod .= "\n\n";
176   if (my $desc = $prereq_desc{$module}) {
177    1 while chomp $desc;
178    $pod .= "$desc\n\n";
179   }
180  }
181  $pod .= '=back';
182
183  return $pod;
184 }
185
186 sub deplist_to_perl {
187  my @deplist = @_;
188  return '{ }' unless @deplist;
189
190  my $len = List::Util::max(
191   map length, @deplist[grep not($_ % 2), 0 .. $#deplist ]
192  );
193
194  my $perl = "{\n";
195  while (@deplist) {
196   my ($module, $version) = splice @deplist, 0, 2;
197   my $pad = $len + 1 - length $module;
198   $perl  .= sprintf " '%s'%*s=> '%s',\n", $module, $pad, ' ', $version;
199  }
200  $perl .= '}';
201
202  return $perl;
203 }
204
205 sub sorthr ($) {
206  my $hr = shift;
207  map { $_ => $hr->{$_} } sort keys %$hr;
208 }
209
210 # Make sure no package FOO statement appears in this file.
211 my $package_statement = join ' ', 'package',
212                                    $task_pkg;
213
214 my %vars = (
215  TARGET_PKG             => $target_pkg,
216  TARGET_VERSION         => $target_version,
217  TASK_PKG               => $task_pkg,
218  PACKAGE_TASK_PKG       => $package_statement,
219  TASK_VERSION           => $new_task_version,
220  PERL_PREREQ            => $prereqs{perl},
221  CONFIGURE_PREREQS_POD  => deplist_to_pod(sorthr $prereqs{configure}),
222  BUILD_PREREQS_POD      => deplist_to_pod(sorthr $prereqs{build}),
223  RUN_PREREQS_POD        => deplist_to_pod(
224   $target_pkg => $target_version,
225   'perl'      => $prereqs{perl},
226   sorthr $prereqs{run}
227  ),
228  CONFIGURE_PREREQS_PERL => deplist_to_perl(sorthr $prereqs{configure}),
229  BUILD_PREREQS_PERL     => deplist_to_perl(sorthr $prereqs{build}),
230  RUN_PREREQS_PERL       => deplist_to_perl(
231   sorthr $prereqs{run},
232   $target_pkg => $target_version,
233  ),
234  TESTED_PREREQS         => deplist_to_perl(sorthr $prereqs{run}),
235  BUG_EMAIL              => $bug_email,
236  BUG_QUEUE              => $bug_queue,
237  YEARS                  => $years,
238 );
239
240 my %templates = (
241  $task_file => <<'TEMPLATE',
242 __PACKAGE_TASK_PKG__;
243
244 use strict;
245 use warnings;
246
247 =head1 NAME
248
249 __TASK_PKG__ - Install __TARGET_PKG__ and its recommended dependencies.
250
251 =head1 VERSION
252
253 Version __TASK_VERSION__
254
255 =cut
256
257 our $VERSION = '__TASK_VERSION__';
258
259 =head1 SYNOPSIS
260
261     $ cpan __TASK_PKG__
262     $ cpanp -i __TASK_PKG__
263     $ cpanm __TASK_PKG__
264
265 =head1 DESCRIPTION
266
267 This task module lets you easily install L<__TARGET_PKG__> __TARGET_VERSION__ and all its recommended dependencies.
268
269 =head1 DEPENDENCIES
270
271 =head2 Configure-time dependencies
272
273 __CONFIGURE_PREREQS_POD__
274
275 =head2 Build-time and test-time dependencies
276
277 __BUILD_PREREQS_POD__
278
279 =head2 Run-time dependencies
280
281 __RUN_PREREQS_POD__
282
283 =head1 AUTHOR
284
285 Vincent Pit, C<< <perl at profvince.com> >>, L<http://www.profvince.com>.
286
287 You can contact me by mail or on C<irc.perl.org> (vincent).
288
289 =head1 BUGS
290
291 Please report any bugs or feature requests to C<__BUG_EMAIL__>, or through the web interface at L<__BUG_QUEUE__>.
292 I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
293
294 =head1 SUPPORT
295
296 You can find documentation for this module with the perldoc command.
297
298     perldoc __TASK_PKG__
299
300 =head1 COPYRIGHT & LICENSE
301
302 Copyright __YEARS__ Vincent Pit, all rights reserved.
303
304 This program is free software; you can redistribute it and/or modify it
305 under the same terms as Perl itself.
306
307 =cut
308
309 1; # End of __TASK_PKG__
310 TEMPLATE
311  # ----------------------------------------------------------------------------
312  'Makefile.PL' => <<'TEMPLATE',
313 use __PERL_PREREQ__;
314
315 use strict;
316 use warnings;
317 use ExtUtils::MakeMaker;
318
319 my $dist = 'Task-Devel-Cover-Recommended';
320
321 (my $name = $dist) =~ s{-}{::}g;
322
323 (my $file = $dist) =~ s{-}{/}g;
324 $file = "lib/$file.pm";
325
326 my $CONFIGURE_PREREQS = __CONFIGURE_PREREQS_PERL__;
327
328 my $BUILD_PREREQS = __BUILD_PREREQS_PERL__;
329
330 my $RUN_PREREQS = __RUN_PREREQS_PERL__;
331
332 my %META = (
333  configure_requires => $CONFIGURE_PREREQS,
334  build_requires     => $BUILD_PREREQS,
335  dynamic_config     => 0,
336  resources          => {
337   bugtracker => "http://rt.cpan.org/NoAuth/ReportBug.html?Queue=$dist",
338   homepage   => "http://search.cpan.org/dist/$dist/",
339   license    => 'http://dev.perl.org/licenses/',
340   repository => "http://git.profvince.com/?p=perl%2Fmodules%2F$dist.git",
341  },
342 );
343
344 WriteMakefile(
345  NAME             => $name,
346  AUTHOR           => 'Vincent Pit <perl@profvince.com>',
347  LICENSE          => 'perl',
348  VERSION_FROM     => $file,
349  ABSTRACT_FROM    => $file,
350  PL_FILES         => {},
351  PREREQ_PM        => $RUN_PREREQS,
352  MIN_PERL_VERSION => '__PERL_PREREQ__',
353  META_MERGE       => \%META,
354  dist             => {
355   PREOP    => "pod2text -u $file > \$(DISTVNAME)/README",
356   COMPRESS => 'gzip -9f', SUFFIX => 'gz'
357  },
358  clean            => {
359   FILES => "$dist-* *.gcov *.gcda *.gcno cover_db Debian_CPANTS.txt*"
360  }
361 );
362 TEMPLATE
363  # ----------------------------------------------------------------------------
364  't/01-deps.t' => <<'TEMPLATE',
365 #!perl
366
367 use strict;
368 use warnings;
369
370 use Test::More;
371
372 my $TESTED_PREREQS = __TESTED_PREREQS__;
373
374 plan tests => keys(%$TESTED_PREREQS) + 1;
375
376 my @tests = map [ $_ => $TESTED_PREREQS->{$_} ], keys %$TESTED_PREREQS;
377 push @tests, [ '__TARGET_PKG__' => '__TARGET_VERSION__' ];
378
379 for my $test (@tests) {
380  my ($module, $version) = @$test;
381  local $@;
382  if ($version && $version !~ /^[0._]*$/) {
383   eval "use $module $version ()";
384   is $@, '', "$module v$version is available";
385  } else {
386   eval "use $module ()";
387   is $@, '', "any version of $module is available";
388  }
389 }
390 TEMPLATE
391 );
392
393 my $valid_keys = join '|', keys %vars;
394 $valid_keys    = qr/$valid_keys/;
395
396 for my $file (sort keys %templates) {
397  my $template = $templates{$file};
398  $template =~ s/\b__($valid_keys)__\b/$vars{$1}/go;
399
400  open my $fh, '>', $file;
401  print $fh $template;
402  close $fh;
403 }