16 use Parse::CPAN::Meta;
19 use Parse::CPAN::Packages::Fast;
21 my $target_dist = 'Devel-Cover';
22 my $first_year = 2012;
23 my $cpan_mirror = 'http://www.cpan.org';
27 'Test::Differences' => 1,
31 '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.',
35 getopts 'n' => \%opts;
40 my $home = File::HomeDir->my_home;
41 my $pkgs = File::Spec->catfile($home, qw<.cpanplus 02packages.details.txt.gz>);
42 my $pcp = Parse::CPAN::Packages::Fast->new($pkgs);
44 my $d = $pcp->latest_distribution($dist);
45 die "Could not find distribution '$dist' on the CPAN" unless $d;
53 $cpan_mirror =~s{/+$}{}g;
54 my $cpanid = $d->cpanid;
55 my ($cp, $c) = $cpanid =~ /^((.).)/;
56 my $uri = join '/', $cpan_mirror, 'authors', 'id', $c, $cp, $cpanid,
59 my $tmp_dir = File::Temp->newdir;
60 # Force symlinks resolution
61 my $tmp_dirname = Cwd::abs_path($tmp_dir->dirname);
63 my $ff = File::Fetch->new(uri => $uri);
64 my $archive = $ff->fetch(to => $tmp_dirname);
65 die $ff->error unless $archive;
67 my $ae = Archive::Extract->new(archive => $archive);
68 $ae->extract(to => $tmp_dirname) or die $ae->error;
71 map { File::Spec->catfile($tmp_dirname, $_) => 1 }
74 my $abs_extract_path = Cwd::abs_path($ae->extract_path);
75 my @meta_candidates = map File::Spec->catfile($abs_extract_path, $_),
76 qw<META.json META.yml>;
78 for my $file (@meta_candidates) {
79 if ($files->{$file}) {
84 die 'No META file for ' . $d->distvname . "\n" unless $meta_file;
86 return Parse::CPAN::Meta->load_file($meta_file);
89 my $latest_target = get_latest_dist($target_dist);
90 my $target_version = $latest_target->version;
91 my $meta = get_dist_meta($latest_target);
94 configure => [ qw<configure> ],
95 build => [ qw<build test> ],
96 run => [ qw<runtime> ],
98 my %meta_phase_relationships = (
99 configure => [ qw<requires> ],
100 build => [ qw<requires> ],
101 test => [ qw<requires> ],
102 runtime => [ qw<requires recommends suggests> ],
107 'ExtUtils::MakeMaker' => '0',
110 'ExtUtils::MakeMaker' => '0',
116 for my $eumm_phase (keys %eumm_phases) {
117 my $prereqs = $prereqs{$eumm_phase} ||= { };
118 my $skip = $prereq_skip{$eumm_phase};
120 for my $meta_phase (@{$eumm_phases{$eumm_phase}}) {
122 for my $type (@{$meta_phase_relationships{$meta_phase}}) {
123 my $phase_prereqs = $meta->{prereqs}{$meta_phase}{$type};
124 next unless $phase_prereqs;
126 while (my ($module, $version) = each %$phase_prereqs) {
127 next if $skip->{$module};
129 if ($module eq 'perl') {
130 if (not $prereqs{perl} or $prereqs{perl} < $version) {
131 $prereqs{perl} = $version;
133 } elsif (not exists $prereqs->{$module} or
134 version->parse($prereqs->{$module}) < version->parse($version)) {
135 $prereqs->{$module} = $version;
142 (my $target_pkg = $target_dist) =~ s/-/::/g;
143 my $task_pkg = "Task::${target_pkg}::Recommended";
144 (my $task_file = "lib/$task_pkg.pm") =~ s{::}{/}g;
145 my $years = join ',', $first_year .. ((gmtime)[5] + 1900);
147 my $old_task_version = '0.0.0';
149 open my $old_fh, '<', $task_file;
151 if (/our\s*\$VERSION\s*=\s*(.*);/) {
152 $old_task_version = $1;
153 $old_task_version =~ s/^(['"])(.*)\1$/$2/;
158 my ($old_task_revision) = $old_task_version =~ /([0-9]+)$/;
160 my $new_task_version = version->parse($target_version)->normal;
161 my $new_task_revision = $old_task_revision;
163 and version->parse($new_task_version) <= version->parse($old_task_version)) {
164 ++$new_task_revision;
166 if (($target_version =~ tr/.//) < 2) {
167 my @components = split /\./, $new_task_version;
168 $components[2] = $new_task_revision;
169 $new_task_version = join '.', @components;
171 $new_task_version .= ".$new_task_revision";
174 (my $bug_queue = $task_pkg) =~ s/::/-/g;
175 my $bug_email = "bug-\L$bug_queue\E at rt.cpan.org";
176 $bug_queue = "http://rt.cpan.org/NoAuth/ReportBug.html?Queue=$bug_queue";
180 return 'None.' unless @deplist;
182 my $pod = "=over 4\n\n";
184 my ($module, $version) = splice @deplist, 0, 2;
185 my $X = $module eq 'perl' ? 'C' : 'L';
186 $pod .= "=item *\n\n$X<$module>";
187 $pod .= " $version" if $version;
189 if (my $desc = $prereq_desc{$module}) {
199 sub deplist_to_perl {
201 return '{ }' unless @deplist;
203 my $len = List::Util::max(
204 map length, @deplist[grep not($_ % 2), 0 .. $#deplist]
209 my ($module, $version) = splice @deplist, 0, 2;
210 my $pad = $len + 1 - length $module;
211 $perl .= sprintf " '%s'%*s=> '%s',\n", $module, $pad, ' ', $version;
220 map { $_ => $hr->{$_} } sort keys %$hr;
223 # Make sure no package FOO statement appears in this file.
224 my $package_statement = join ' ', 'package',
228 TARGET_PKG => $target_pkg,
229 TARGET_VERSION => $target_version,
230 TASK_PKG => $task_pkg,
231 PACKAGE_TASK_PKG => $package_statement,
232 TASK_VERSION => $new_task_version,
233 PERL_PREREQ => $prereqs{perl},
234 CONFIGURE_PREREQS_POD => deplist_to_pod(sorthr $prereqs{configure}),
235 BUILD_PREREQS_POD => deplist_to_pod(sorthr $prereqs{build}),
236 RUN_PREREQS_POD => deplist_to_pod(
237 $target_pkg => $target_version,
238 'perl' => $prereqs{perl},
241 CONFIGURE_PREREQS_PERL => deplist_to_perl(sorthr $prereqs{configure}),
242 BUILD_PREREQS_PERL => deplist_to_perl(sorthr $prereqs{build}),
243 RUN_PREREQS_PERL => deplist_to_perl(
244 sorthr $prereqs{run},
245 $target_pkg => $target_version,
247 TESTED_PREREQS => deplist_to_perl(sorthr $prereqs{run}),
248 BUG_EMAIL => $bug_email,
249 BUG_QUEUE => $bug_queue,
254 $task_file => <<'TEMPLATE',
255 __PACKAGE_TASK_PKG__;
262 __TASK_PKG__ - Install __TARGET_PKG__ and its recommended dependencies.
266 Version __TASK_VERSION__
270 our $VERSION = '__TASK_VERSION__';
275 $ cpanp -i __TASK_PKG__
280 This task module lets you easily install L<__TARGET_PKG__> __TARGET_VERSION__ and all its recommended dependencies.
284 \=head2 Configure-time dependencies
286 __CONFIGURE_PREREQS_POD__
288 \=head2 Build-time and test-time dependencies
290 __BUILD_PREREQS_POD__
292 \=head2 Run-time dependencies
298 Note that run-time dependencies that are only recommended by __TARGET_PKG__ may not yet be installed at the time __TARGET_PKG__ is tested, as there is no explicit dependency link between them and in that case most CPAN clients default to install prerequisites in alphabetic order.
299 However, they will be installed when __TASK_PKG__ is, thus will be available when you actually use __TARGET_PKG__.
303 Vincent Pit, C<< <perl at profvince.com> >>, L<http://www.profvince.com>.
305 You can contact me by mail or on C<irc.perl.org> (vincent).
309 Please report any bugs or feature requests to C<__BUG_EMAIL__>, or through the web interface at L<__BUG_QUEUE__>.
310 I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
314 You can find documentation for this module with the perldoc command.
318 \=head1 COPYRIGHT & LICENSE
320 Copyright __YEARS__ Vincent Pit, all rights reserved.
322 This program is free software; you can redistribute it and/or modify it
323 under the same terms as Perl itself.
327 1; # End of __TASK_PKG__
329 # ----------------------------------------------------------------------------
330 'Makefile.PL' => <<'TEMPLATE',
335 use ExtUtils::MakeMaker;
337 my $dist = 'Task-Devel-Cover-Recommended';
339 (my $name = $dist) =~ s{-}{::}g;
341 (my $file = $dist) =~ s{-}{/}g;
342 $file = "lib/$file.pm";
344 my $CONFIGURE_PREREQS = __CONFIGURE_PREREQS_PERL__;
346 my $BUILD_PREREQS = __BUILD_PREREQS_PERL__;
348 my $RUN_PREREQS = __RUN_PREREQS_PERL__;
351 configure_requires => $CONFIGURE_PREREQS,
352 build_requires => $BUILD_PREREQS,
355 bugtracker => "http://rt.cpan.org/NoAuth/ReportBug.html?Queue=$dist",
356 homepage => "http://search.cpan.org/dist/$dist/",
357 license => 'http://dev.perl.org/licenses/',
358 repository => "http://git.profvince.com/?p=perl%2Fmodules%2F$dist.git",
364 AUTHOR => 'Vincent Pit <perl@profvince.com>',
366 VERSION_FROM => $file,
367 ABSTRACT_FROM => $file,
369 BUILD_REQUIRES => $BUILD_PREREQS,
370 PREREQ_PM => $RUN_PREREQS,
371 MIN_PERL_VERSION => '__PERL_PREREQ__',
372 META_MERGE => \%META,
374 PREOP => "pod2text -u $file > \$(DISTVNAME)/README",
375 COMPRESS => 'gzip -9f', SUFFIX => 'gz'
378 FILES => "$dist-* *.gcov *.gcda *.gcno cover_db Debian_CPANTS.txt*"
382 # ----------------------------------------------------------------------------
383 't/01-deps.t' => <<'TEMPLATE',
391 my $TESTED_PREREQS = __TESTED_PREREQS__;
393 plan tests => keys(%$TESTED_PREREQS) + 1;
395 my @tests = map [ $_ => $TESTED_PREREQS->{$_} ], keys %$TESTED_PREREQS;
396 push @tests, [ '__TARGET_PKG__' => '__TARGET_VERSION__' ];
398 for my $test (@tests) {
399 my ($module, $version) = @$test;
401 if ($version && $version !~ /^[0._]*$/) {
402 eval "use $module $version ()";
403 is $@, '', "$module v$version is available";
405 eval "use $module ()";
406 is $@, '', "any version of $module is available";
412 $templates{$task_file} =~ s/^\\=/=/mg;
414 my $valid_keys = join '|', keys %vars;
415 $valid_keys = qr/$valid_keys/;
417 for my $file (sort keys %templates) {
418 my $template = $templates{$file};
419 $template =~ s/\b__($valid_keys)__\b/$vars{$1}/go;
421 open my $fh, '>', $file;