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/;
159 my $new_task_version;
162 $new_task_version = $old_task_version;
164 my ($old_target_version, $old_task_revision)
165 = $old_task_version =~ /(.*)\.([0-9]+)$/;
166 my $new_task_revision;
167 if (version->parse($target_version) > version->parse($old_target_version)) {
168 $new_task_revision = 0;
170 $new_task_revision = $old_task_revision + 1;
172 $new_task_version = version->parse($target_version)->normal;
173 if (($target_version =~ tr/.//) < 2) {
174 my @components = split /\./, $new_task_version;
175 $components[2] = $new_task_revision;
176 $new_task_version = join '.', @components;
178 $new_task_version .= ".$new_task_revision";
182 (my $bug_queue = $task_pkg) =~ s/::/-/g;
183 my $bug_email = "bug-\L$bug_queue\E at rt.cpan.org";
184 $bug_queue = "http://rt.cpan.org/NoAuth/ReportBug.html?Queue=$bug_queue";
188 return 'None.' unless @deplist;
190 my $pod = "=over 4\n\n";
192 my ($module, $version) = splice @deplist, 0, 2;
193 my $X = $module eq 'perl' ? 'C' : 'L';
194 $pod .= "=item *\n\n$X<$module>";
195 $pod .= " $version" if $version;
197 if (my $desc = $prereq_desc{$module}) {
207 sub deplist_to_perl {
209 return '{ }' unless @deplist;
211 my $len = List::Util::max(
212 map length, @deplist[grep not($_ % 2), 0 .. $#deplist]
217 my ($module, $version) = splice @deplist, 0, 2;
218 my $pad = $len + 1 - length $module;
219 $perl .= sprintf " '%s'%*s=> '%s',\n", $module, $pad, ' ', $version;
228 map { $_ => $hr->{$_} } sort keys %$hr;
231 # Make sure no package FOO statement appears in this file.
232 my $package_statement = join ' ', 'package',
236 TARGET_PKG => $target_pkg,
237 TARGET_VERSION => $target_version,
238 TASK_PKG => $task_pkg,
239 PACKAGE_TASK_PKG => $package_statement,
240 TASK_VERSION => $new_task_version,
241 PERL_PREREQ => $prereqs{perl},
242 CONFIGURE_PREREQS_POD => deplist_to_pod(sorthr $prereqs{configure}),
243 BUILD_PREREQS_POD => deplist_to_pod(sorthr $prereqs{build}),
244 RUN_PREREQS_POD => deplist_to_pod(
245 $target_pkg => $target_version,
246 'perl' => $prereqs{perl},
249 CONFIGURE_PREREQS_PERL => deplist_to_perl(sorthr $prereqs{configure}),
250 BUILD_PREREQS_PERL => deplist_to_perl(sorthr $prereqs{build}),
251 RUN_PREREQS_PERL => deplist_to_perl(
252 sorthr $prereqs{run},
253 $target_pkg => $target_version,
255 TESTED_PREREQS => deplist_to_perl(sorthr $prereqs{run}),
256 BUG_EMAIL => $bug_email,
257 BUG_QUEUE => $bug_queue,
262 $task_file => <<'TEMPLATE',
263 __PACKAGE_TASK_PKG__;
270 __TASK_PKG__ - Install __TARGET_PKG__ and its recommended dependencies.
274 Version __TASK_VERSION__
278 our $VERSION = '__TASK_VERSION__';
283 $ cpanp -i __TASK_PKG__
288 This task module lets you easily install L<__TARGET_PKG__> __TARGET_VERSION__ and all its recommended dependencies.
292 \=head2 Configure-time dependencies
294 __CONFIGURE_PREREQS_POD__
296 \=head2 Build-time and test-time dependencies
298 __BUILD_PREREQS_POD__
300 \=head2 Run-time dependencies
306 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.
307 However, they will be installed when __TASK_PKG__ is, thus will be available when you actually use __TARGET_PKG__.
311 Vincent Pit, C<< <perl at profvince.com> >>, L<http://www.profvince.com>.
313 You can contact me by mail or on C<irc.perl.org> (vincent).
317 Please report any bugs or feature requests to C<__BUG_EMAIL__>, or through the web interface at L<__BUG_QUEUE__>.
318 I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
322 You can find documentation for this module with the perldoc command.
326 \=head1 COPYRIGHT & LICENSE
328 Copyright __YEARS__ Vincent Pit, all rights reserved.
330 This program is free software; you can redistribute it and/or modify it
331 under the same terms as Perl itself.
335 1; # End of __TASK_PKG__
337 # ----------------------------------------------------------------------------
338 'Makefile.PL' => <<'TEMPLATE',
343 use ExtUtils::MakeMaker;
345 my $dist = 'Task-Devel-Cover-Recommended';
347 (my $name = $dist) =~ s{-}{::}g;
349 (my $file = $dist) =~ s{-}{/}g;
350 $file = "lib/$file.pm";
352 my $CONFIGURE_PREREQS = __CONFIGURE_PREREQS_PERL__;
354 my $BUILD_PREREQS = __BUILD_PREREQS_PERL__;
356 my $RUN_PREREQS = __RUN_PREREQS_PERL__;
359 configure_requires => $CONFIGURE_PREREQS,
360 build_requires => $BUILD_PREREQS,
363 bugtracker => "http://rt.cpan.org/NoAuth/ReportBug.html?Queue=$dist",
364 homepage => "http://search.cpan.org/dist/$dist/",
365 license => 'http://dev.perl.org/licenses/',
366 repository => "http://git.profvince.com/?p=perl%2Fmodules%2F$dist.git",
372 AUTHOR => 'Vincent Pit <perl@profvince.com>',
374 VERSION_FROM => $file,
375 ABSTRACT_FROM => $file,
377 BUILD_REQUIRES => $BUILD_PREREQS,
378 PREREQ_PM => $RUN_PREREQS,
379 MIN_PERL_VERSION => '__PERL_PREREQ__',
380 META_MERGE => \%META,
382 PREOP => "pod2text -u $file > \$(DISTVNAME)/README",
383 COMPRESS => 'gzip -9f', SUFFIX => 'gz'
386 FILES => "$dist-* *.gcov *.gcda *.gcno cover_db Debian_CPANTS.txt*"
390 # ----------------------------------------------------------------------------
391 't/01-deps.t' => <<'TEMPLATE',
399 my $TESTED_PREREQS = __TESTED_PREREQS__;
401 plan tests => keys(%$TESTED_PREREQS) + 1;
403 my @tests = map [ $_ => $TESTED_PREREQS->{$_} ], keys %$TESTED_PREREQS;
404 push @tests, [ '__TARGET_PKG__' => '__TARGET_VERSION__' ];
406 for my $test (@tests) {
407 my ($module, $version) = @$test;
409 if ($version && $version !~ /^[0._]*$/) {
410 eval "use $module $version ()";
411 is $@, '', "$module v$version is available";
413 eval "use $module ()";
414 is $@, '', "any version of $module is available";
420 $templates{$task_file} =~ s/^\\=/=/mg;
422 my $valid_keys = join '|', keys %vars;
423 $valid_keys = qr/$valid_keys/;
425 for my $file (sort keys %templates) {
426 my $template = $templates{$file};
427 $template =~ s/\b__($valid_keys)__\b/$vars{$1}/go;
429 open my $fh, '>', $file;