]> git.vpit.fr Git - perl/modules/CPANPLUS-Dist-Gentoo.git/blob - lib/CPANPLUS/Dist/Gentoo.pm
Enforce the right version of the perl dependency whenever possible
[perl/modules/CPANPLUS-Dist-Gentoo.git] / lib / CPANPLUS / Dist / Gentoo.pm
1 package CPANPLUS::Dist::Gentoo;
2
3 use strict;
4 use warnings;
5
6 use Cwd        ();
7 use List::Util qw/reduce/;
8 use File::Copy ();
9 use File::Path ();
10 use File::Spec;
11
12 use IPC::Cmd          ();
13 use Parse::CPAN::Meta ();
14
15 use CPANPLUS::Error ();
16
17 use base qw/CPANPLUS::Dist::Base/;
18
19 use CPANPLUS::Dist::Gentoo::Atom;
20 use CPANPLUS::Dist::Gentoo::Maps;
21
22 =head1 NAME
23
24 CPANPLUS::Dist::Gentoo - CPANPLUS backend generating Gentoo ebuilds.
25
26 =head1 VERSION
27
28 Version 0.09
29
30 =cut
31
32 our $VERSION = '0.09';
33
34 =head1 SYNOPSIS
35
36     cpan2dist --format=CPANPLUS::Dist::Gentoo \
37               --dist-opts overlay=/usr/local/portage \
38               --dist-opts distdir=/usr/portage/distfiles \
39               --dist-opts manifest=yes \
40               --dist-opts keywords=x86 \
41               --dist-opts header="# Copyright 1999-2008 Gentoo Foundation" \
42               --dist-opts footer="# End" \
43               Any::Module You::Like
44
45 =head1 DESCRPITON
46
47 This module is a CPANPLUS backend that recursively generates Gentoo ebuilds for a given package in the specified overlay (defaults to F</usr/local/portage>), updates the manifest, and even emerges it (together with its dependencies) if the user requires it.
48 You need write permissions on the directory where Gentoo fetches its source files (usually F</usr/portage/distfiles>).
49 The valid C<KEYWORDS> for the generated ebuilds are by default those given in C<ACCEPT_KEYWORDS>, but you can specify your own with the C<keywords> dist-option.
50
51 The generated ebuilds are placed into the C<perl-gcpanp> category.
52 They favour depending on a C<virtual>, on C<perl-core>, C<dev-perl> or C<perl-gcpan> (in that order) rather than C<perl-gcpanp>.
53
54 =head1 INSTALLATION
55
56 Before installing this module, you should append C<perl-gcpanp> to your F</etc/portage/categories> file.
57
58 You have two ways for installing this module :
59
60 =over 4
61
62 =item *
63
64 Use the perl overlay located at L<http://git.overlays.gentoo.org/gitweb/?p=proj/perl-overlay.git>.
65 It contains an ebuild for L<CPANPLUS::Dist::Gentoo>.
66
67 =item *
68
69 Bootstrap an ebuild for L<CPANPLUS::Dist::Gentoo> using itself.
70 Note that if your Gentoo system C<perl> is C<5.8.x>, L<CPANPLUS> and its dependencies are not installed and not even available in the main portage tree.
71 So you need to bootstrap them as well.
72
73 First, fetch tarballs for L<CPANPLUS> and L<CPANPLUS::Dist::Gentoo> :
74
75     $ cd /tmp
76     $ wget http://search.cpan.org/CPAN/authors/id/B/BI/BINGOS/CPANPLUS-0.9003.tar.gz
77     $ wget http://search.cpan.org/CPAN/authors/id/V/VP/VPIT/CPANPLUS-Dist-Gentoo-0.09.tar.gz
78
79 Log in as root and unpack them in e.g. your home directory :
80
81     # cd
82     # tar xzf /tmp/CPANPLUS-0.9003.tar.gz
83     # tar xzf /tmp/CPANPLUS-Dist-Gentoo-0.09.tar.gz
84
85 Set up environment variables so that the toolchain is temporarily available :
86
87     # export OLDPATH=$PATH
88     # export PATH=/root/CPANPLUS-0.9003/bin:$PATH
89     # export PERL5LIB=/root/CPANPLUS-Dist-Gentoo-0.09/blib/lib:/root/CPANPLUS-0.9003/lib:/root/CPANPLUS-0.9003/inc/bundle
90
91 Make sure you don't have an old C<.cpanplus> configuration visible :
92
93     # [ -d /root/.cpanplus ] && mv /root/.cpanplus{,.bak}
94
95 Bootstrap L<CPANPLUS> :
96
97     # cd /root/CPANPLUS-Dist-Gentoo-0.09
98     # samples/g-cpanp CPANPLUS
99
100 Reset the environment :
101
102     # export PATH=$OLDPATH
103     # unset PERL5LIB OLDPATH
104
105 Emerge L<CPANPLUS> with the ebuilds you've just generated :
106
107     # emerge -tv CPANPLUS
108
109 As of september 2009, C<podlators> and C<ExtUtils-MakeMaker> may fail to emerge due to collisions.
110 You can work around this by disabling the C<protect-owned> C<FEATURE> for them :
111
112     # FEATURES="-protect-owned" emerge podlators
113     # FEATURES="-protect-owned" emerge ExtUtils-MakeMaker
114
115 You may need to run each of these commands two times for them to succeed.
116
117 At this point, you can bootstrap L<CPANPLUS::Dist::Gentoo> using the system L<CPANPLUS> :
118
119     # PERL5LIB=/root/CPANPLUS-Dist-Gentoo-0.09/blib/lib samples/g-cpanp CPANPLUS::Dist::Gentoo
120     # emerge -tv CPANPLUS-Dist-Gentoo
121
122 =back
123
124 =head1 METHODS
125
126 This module inherits all the methods from L<CPANPLUS::Dist::Base>.
127 Please refer to its documentation for precise information on what's done at each step.
128
129 =cut
130
131 use constant CATEGORY => 'perl-gcpanp';
132
133 my $overlays;
134 my $default_keywords;
135 my $default_distdir;
136 my $main_portdir;
137
138 my %forced;
139
140 my $unquote = sub {
141  my $s = shift;
142  $s =~ s/^["']*//;
143  $s =~ s/["']*$//;
144  return $s;
145 };
146
147 my $format_available;
148
149 sub format_available {
150  return $format_available if defined $format_available;
151
152  for my $prog (qw/emerge ebuild/) {
153   unless (IPC::Cmd::can_run($prog)) {
154    __PACKAGE__->_abort("$prog is required to write ebuilds");
155    return $format_available = 0;
156   }
157  }
158
159  if (IPC::Cmd->can_capture_buffer) {
160   my $buffers;
161   my ($success, $errmsg) = IPC::Cmd::run(
162    command => [ qw/emerge --info/ ],
163    verbose => 0,
164    buffer  => \$buffers,
165   );
166   if ($success) {
167    if ($buffers =~ /^PORTDIR_OVERLAY=(.*)$/m) {
168     $overlays = [ map Cwd::abs_path($_), split ' ', $unquote->($1) ];
169    }
170    if ($buffers =~ /^ACCEPT_KEYWORDS=(.*)$/m) {
171     $default_keywords = [ split ' ', $unquote->($1) ];
172    }
173    if ($buffers =~ /^DISTDIR=(.*)$/m) {
174     $default_distdir = Cwd::abs_path($unquote->($1));
175    }
176    if ($buffers =~ /^PORTDIR=(.*)$/m) {
177     $main_portdir = Cwd::abs_path($unquote->($1));
178    }
179   } else {
180    __PACKAGE__->_abort($errmsg);
181   }
182  }
183
184  $default_keywords = [ 'x86' ] unless defined $default_keywords;
185  $default_distdir  = '/usr/portage/distfiles' unless defined $default_distdir;
186
187  return $format_available = 1;
188 }
189
190 sub init {
191  my ($self) = @_;
192  my $stat = $self->status;
193  my $conf = $self->parent->parent->configure_object;
194
195  $stat->mk_accessors(qw/name version author distribution desc uri src license
196                         meta min_perl
197                         fetched_arch requires
198                         ebuild_name ebuild_version ebuild_dir ebuild_file
199                         portdir_overlay
200                         overlay distdir keywords do_manifest header footer
201                         force verbose/);
202
203  $stat->force($conf->get_conf('force'));
204  $stat->verbose($conf->get_conf('verbose'));
205
206  return 1;
207 }
208
209 sub prepare {
210  my $self = shift;
211  my $mod  = $self->parent;
212  my $stat = $self->status;
213  my $int  = $mod->parent;
214  my $conf = $int->configure_object;
215
216  my %opts = @_;
217
218  my $OK   = sub { $stat->prepared(1); 1 };
219  my $FAIL = sub { $stat->prepared(0); $self->_abort(@_) if @_; 0 };
220  my $SKIP = sub { $stat->prepared(1); $stat->created(1); $self->_skip(@_) if @_; 1 };
221
222  my $keywords = delete $opts{keywords};
223  if (defined $keywords) {
224   $keywords = [ split ' ', $keywords ];
225  } else {
226   $keywords = $default_keywords;
227  }
228  $stat->keywords($keywords);
229
230  my $manifest = delete $opts{manifest};
231  $manifest = 1 unless defined $manifest;
232  $manifest = 0 if $manifest =~ /^\s*no?\s*$/i;
233  $stat->do_manifest($manifest);
234
235  my $header = delete $opts{header};
236  if (defined $header) {
237   1 while chomp $header;
238   $header .= "\n\n";
239  } else {
240   my $year = (localtime)[5] + 1900;
241   $header = <<"  DEF_HEADER";
242 # Copyright 1999-$year Gentoo Foundation
243 # Distributed under the terms of the GNU General Public License v2
244 # \$Header: \$
245   DEF_HEADER
246  }
247  $stat->header($header);
248
249  my $footer = delete $opts{footer};
250  if (defined $footer) {
251   $footer = "\n" . $footer;
252  } else {
253   $footer = '';
254  }
255  $stat->footer($footer);
256
257  my $overlay = delete $opts{overlay};
258  $overlay = (defined $overlay) ? Cwd::abs_path($overlay) : '/usr/local/portage';
259  $stat->overlay($overlay);
260
261  my $distdir = delete $opts{distdir};
262  $distdir = (defined $distdir) ? Cwd::abs_path($distdir) : $default_distdir;
263  $stat->distdir($distdir);
264
265  return $FAIL->("distdir isn't writable") if $stat->do_manifest && !-w $distdir;
266
267  $stat->fetched_arch($mod->status->fetch);
268
269  my $cur = File::Spec->curdir();
270  my $portdir_overlay;
271  for (@$overlays) {
272   if ($_ eq $overlay or File::Spec->abs2rel($overlay, $_) eq $cur) {
273    $portdir_overlay = [ @$overlays ];
274    last;
275   }
276  }
277  $portdir_overlay = [ @$overlays, $overlay ] unless $portdir_overlay;
278  $stat->portdir_overlay($portdir_overlay);
279
280  my $name = $mod->package_name;
281  $stat->name($name);
282
283  my $version = $mod->package_version;
284  $stat->version($version);
285
286  my $author = $mod->author->cpanid;
287  $stat->author($author);
288
289  $stat->distribution($name . '-' . $version);
290
291  $stat->ebuild_version(CPANPLUS::Dist::Gentoo::Maps::version_c2g($version));
292
293  $stat->ebuild_name(CPANPLUS::Dist::Gentoo::Maps::name_c2g($name));
294
295  $stat->ebuild_dir(File::Spec->catdir(
296   $stat->overlay,
297   CATEGORY,
298   $stat->ebuild_name,
299  ));
300
301  my $file = File::Spec->catfile(
302   $stat->ebuild_dir,
303   $stat->ebuild_name . '-' . $stat->ebuild_version . '.ebuild',
304  );
305  $stat->ebuild_file($file);
306
307  if ($stat->force) {
308   # Always generate an ebuild in our category when forcing
309   if ($forced{$file}) {
310    $stat->dist($file);
311    return $SKIP->('Ebuild already forced for', $stat->distribution);
312   }
313   ++$forced{$file};
314   if (-e $file) {
315    unless (-w $file) {
316     $stat->dist($file);
317     return $SKIP->("Can't force rewriting of $file");
318    }
319    1 while unlink $file;
320   }
321  } else {
322   if (my $atom = $self->_cpan2portage($name, $version)) {
323    $stat->dist($atom->ebuild);
324    return $SKIP->('Ebuild already generated for', $stat->distribution);
325   }
326  }
327
328  $stat->prepared(0);
329
330  $self->SUPER::prepare(@_);
331
332  return $FAIL->() unless $stat->prepared;
333
334  my $desc = $mod->description;
335  ($desc = $name) =~ s/-+/::/g unless $desc;
336  $stat->desc($desc);
337
338  $stat->uri('http://search.cpan.org/dist/' . $name);
339
340  $author =~ /^(.)(.)/ or return $FAIL->('Wrong author name');
341  $stat->src("mirror://cpan/modules/by-authors/id/$1/$1$2/$author/" . $mod->package);
342
343  $stat->license($self->intuit_license);
344
345  my $prereqs = $mod->status->requires;
346  my @requires;
347  for my $prereq (sort keys %$prereqs) {
348   next if $prereq =~ /^perl(?:-|\z)/;
349   my $obj = $int->module_tree($prereq);
350   next unless $obj; # Not in the module tree (e.g. Config)
351   next if $obj->package_is_perl_core;
352   {
353    my $version;
354    if ($prereqs->{$prereq}) {
355     if ($obj->installed_version && $obj->installed_version < $obj->version) {
356      $version = $obj->installed_version;
357     } else {
358      $version = $obj->package_version;
359     }
360    }
361    push @requires, [ $obj->package_name, $version ];
362   }
363  }
364  $stat->requires(\@requires);
365
366  $stat->min_perl(CPANPLUS::Dist::Gentoo::Maps::perl_version_c2g(
367   eval { $self->meta->{requires}->{perl} }
368  ));
369
370  return $OK->();
371 }
372
373 =head2 C<meta>
374
375 Returns the contents of the F<META.yml> or F<META.json> files as parsed by L<Parse::CPAN::Meta>.
376
377 =cut
378
379 sub meta {
380  my $self = shift;
381  my $mod  = $self->parent;
382  my $stat = $self->status;
383
384  my $meta = $stat->meta;
385  return $meta if defined $meta;
386
387  my $extract_dir = $mod->status->extract;
388
389  for my $name (qw/META.json META.yml/) {
390   my $meta_file = File::Spec->catdir($extract_dir, $name);
391   next unless -e $meta_file;
392
393   my $meta = eval { Parse::CPAN::Meta::LoadFile($meta_file) };
394   if (defined $meta) {
395    $stat->meta($meta);
396    return $meta;
397   }
398  }
399
400  return;
401 }
402
403 =head2 C<intuit_license>
404
405 Returns an array reference to a list of Gentoo licences identifiers under which the current distribution is released.
406
407 =cut
408
409 my %dslip_license = (
410  p => 'perl',
411  g => 'gpl',
412  l => 'lgpl',
413  b => 'bsd',
414  a => 'artistic',
415  2 => 'artistic_2',
416 );
417
418 sub intuit_license {
419  my $self = shift;
420  my $mod  = $self->parent;
421
422  my $dslip = $mod->dslip;
423  if (defined $dslip and $dslip =~ /\S{4}(\S)/) {
424   my @licenses = CPANPLUS::Dist::Gentoo::Maps::license_c2g($dslip_license{$1});
425   return \@licenses if @licenses;
426  }
427
428  my $license = $self->meta->{license};
429  if (defined $license) {
430   my @licenses = CPANPLUS::Dist::Gentoo::Maps::license_c2g($license);
431   return \@licenses if @licenses;
432  }
433
434  return [ CPANPLUS::Dist::Gentoo::Maps::license_c2g('perl') ];
435 }
436
437 sub create {
438  my $self = shift;
439  my $stat = $self->status;
440
441  my $file;
442
443  my $OK   = sub {
444   $stat->created(1);
445   $stat->dist($file) if defined $file;
446   1;
447  };
448
449  my $FAIL = sub {
450   $stat->created(0);
451   $stat->dist(undef);
452   $self->_abort(@_) if @_;
453   if (defined $file and -f $file) {
454    1 while unlink $file;
455   }
456   0;
457  };
458
459  unless ($stat->prepared) {
460   return $FAIL->(
461    'Can\'t create', $stat->distribution, 'since it was never prepared'
462   );
463  }
464
465  if ($stat->created) {
466   $self->_skip($stat->distribution, 'was already created');
467   $file = $stat->dist; # Keep the existing one.
468   return $OK->();
469  }
470
471  my $dir = $stat->ebuild_dir;
472  unless (-d $dir) {
473   eval { File::Path::mkpath($dir) };
474   return $FAIL->("mkpath($dir): $@") if $@;
475  }
476
477  $file = $stat->ebuild_file;
478
479  # Create a placeholder ebuild to prevent recursion with circular dependencies.
480  {
481   open my $eb, '>', $file or return $FAIL->("open($file): $!");
482   print $eb "PLACEHOLDER\n";
483  }
484
485  $stat->created(0);
486  $stat->dist(undef);
487
488  $self->SUPER::create(@_);
489
490  return $FAIL->() unless $stat->created;
491
492  {
493   open my $eb, '>', $file or return $FAIL->("open($file): $!");
494   my $source = $self->ebuild_source;
495   return $FAIL->() unless defined $source;
496   print $eb $source;
497  }
498
499  return $FAIL->() if $stat->do_manifest and not $self->update_manifest;
500
501  return $OK->();
502 }
503
504 =head2 C<update_manifest>
505
506 Updates the F<Manifest> file for the ebuild associated to the current dist object.
507
508 =cut
509
510 sub update_manifest {
511  my $self = shift;
512  my $stat = $self->status;
513
514  my $file = $stat->ebuild_file;
515  unless (defined $file and -e $file) {
516   return $self->_abort('The ebuild file is invalid or does not exist');
517  }
518
519  unless (File::Copy::copy($stat->fetched_arch => $stat->distdir)) {
520   return $self->_abort("Couldn\'t copy the distribution file to distdir ($!)");
521  }
522
523  $self->_notify('Adding Manifest entry for', $stat->distribution);
524
525  return $self->_run([ 'ebuild', $file, 'manifest' ], 0);
526 }
527
528 =head2 C<ebuild_source>
529
530 Returns the source of the ebuild for the current dist object, or C<undef> when one of the dependencies couldn't be mapped to an existing ebuild.
531
532 =cut
533
534 sub ebuild_source {
535  my $self = shift;
536  my $stat = $self->status;
537
538  # We must resolve the deps now and not inside prepare because _cpan2portage
539  # has to see the ebuilds already generated for the dependencies of the current
540  # dist.
541  my @requires;
542  for (@{$stat->requires}) {
543   my $atom = $self->_cpan2portage(@$_);
544   unless (defined $atom) {
545    $self->_abort(
546     "Couldn't find an appropriate ebuild for $_->[0] in the portage tree"
547    );
548    return;
549   }
550   push @requires, $atom;
551  }
552
553  my $min_perl = $stat->min_perl;
554  my $perl = CPANPLUS::Dist::Gentoo::Atom->new(
555   category => 'dev-lang',
556   name     => 'perl',
557   (defined $min_perl ? (version => $min_perl, range => '>=') : ()),
558  );
559
560  @requires = CPANPLUS::Dist::Gentoo::Atom->fold($perl, @requires);
561
562  my $d = $stat->header;
563  $d   .= "# Generated by CPANPLUS::Dist::Gentoo version $VERSION\n\n";
564  $d   .= 'MODULE_AUTHOR="' . $stat->author . "\"\ninherit perl-module\n\n";
565  $d   .= 'S="${WORKDIR}/' . $stat->distribution . "\"\n";
566  $d   .= 'DESCRIPTION="' . $stat->desc . "\"\n";
567  $d   .= 'HOMEPAGE="' . $stat->uri . "\"\n";
568  $d   .= 'SRC_URI="' . $stat->src . "\"\n";
569  $d   .= "SLOT=\"0\"\n";
570  $d   .= 'LICENSE="|| ( ' . join(' ', sort @{$stat->license}) . " )\"\n";
571  $d   .= 'KEYWORDS="' . join(' ', sort @{$stat->keywords}) . "\"\n";
572  $d   .= 'RDEPEND="' . join("\n", sort @requires) . "\"\n";
573  $d   .= "DEPEND=\"\${RDEPEND}\"\n";
574  $d   .= "SRC_TEST=\"do\"\n";
575  $d   .= $stat->footer;
576
577  return $d;
578 }
579
580 sub _cpan2portage {
581  my ($self, $name, $version) = @_;
582
583  $name    = CPANPLUS::Dist::Gentoo::Maps::name_c2g($name);
584  $version = CPANPLUS::Dist::Gentoo::Maps::version_c2g($version);
585
586  my @portdirs = ($main_portdir, @{$self->status->portdir_overlay});
587
588  for my $category (qw/virtual perl-core dev-perl perl-gcpan/, CATEGORY) {
589   my $name = ($category eq 'virtual' ? 'perl-' : '') . $name;
590
591   for my $portdir (@portdirs) {
592    my @ebuilds = glob File::Spec->catfile(
593     $portdir,
594     $category,
595     $name,
596     "$name-*.ebuild",
597    ) or next;
598
599    my $last = reduce { $a < $b ? $b : $a } # handles overloading
600                map CPANPLUS::Dist::Gentoo::Atom->new_from_ebuild($_),
601                 @ebuilds;
602    next if defined $version and $last < $version;
603
604    return CPANPLUS::Dist::Gentoo::Atom->new(
605     category => $last->category,
606     name     => $last->name,
607     (defined $version ? (version => $version, range => '>=') : ()),
608     ebuild   => $last->ebuild,
609    );
610   }
611
612  }
613
614  return;
615 }
616
617 sub install {
618  my $self = shift;
619  my $stat = $self->status;
620  my $conf = $self->parent->parent->configure_object;
621
622  my $sudo = $conf->get_program('sudo');
623  my @cmd = ('emerge', '=' . $stat->ebuild_name . '-' . $stat->ebuild_version);
624  unshift @cmd, $sudo if $sudo;
625
626  my $success = $self->_run(\@cmd, 1);
627  $stat->installed($success);
628
629  return $success;
630 }
631
632 sub uninstall {
633  my $self = shift;
634  my $stat = $self->status;
635  my $conf = $self->parent->parent->configure_object;
636
637  my $sudo = $conf->get_program('sudo');
638  my @cmd = ('emerge', '-C', '=' . $stat->ebuild_name . '-' . $stat->ebuild_version);
639  unshift @cmd, $sudo if $sudo;
640
641  my $success = $self->_run(\@cmd, 1);
642  $stat->uninstalled($success);
643
644  return $success;
645 }
646
647 sub _run {
648  my ($self, $cmd, $verbose) = @_;
649  my $stat = $self->status;
650
651  my ($success, $errmsg, $output) = do {
652   local $ENV{PORTDIR_OVERLAY}     = join ' ', @{$stat->portdir_overlay};
653   local $ENV{PORTAGE_RO_DISTDIRS} = $stat->distdir;
654   IPC::Cmd::run(
655    command => $cmd,
656    verbose => $verbose,
657   );
658  };
659
660  unless ($success) {
661   $self->_abort($errmsg);
662   if (not $verbose and defined $output and $stat->verbose) {
663    my $msg = join '', @$output;
664    1 while chomp $msg;
665    CPANPLUS::Error::error($msg);
666   }
667  }
668
669  return $success;
670 }
671
672 sub _abort {
673  my $self = shift;
674
675  CPANPLUS::Error::error("@_ -- aborting");
676
677  return 0;
678 }
679
680 sub _notify {
681  my $self = shift;
682
683  CPANPLUS::Error::msg("@_");
684
685  return 1;
686 }
687
688 sub _skip { shift->_notify(@_, '-- skipping') }
689
690 =head1 DEPENDENCIES
691
692 Gentoo (L<http://gentoo.org>).
693
694 L<CPANPLUS>, L<IPC::Cmd> (core modules since 5.9.5), L<Parse::CPAN::Meta> (since 5.10.1).
695
696 L<Cwd>, L<Carp> (since perl 5), L<File::Path> (5.001), L<File::Copy> (5.002), L<File::Spec> (5.00405), L<List::Util> (5.007003).
697
698 =head1 SEE ALSO
699
700 L<cpan2dist>.
701
702 L<CPANPLUS::Dist::Base>, L<CPANPLUS::Dist::Deb>, L<CPANPLUS::Dist::Mdv>.
703
704 =head1 AUTHOR
705
706 Vincent Pit, C<< <perl at profvince.com> >>, L<http://www.profvince.com>.
707
708 You can contact me by mail or on C<irc.perl.org> (vincent).
709
710 =head1 BUGS
711
712 Please report any bugs or feature requests to C<bug-cpanplus-dist-gentoo at rt.cpan.org>, or through the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=CPANPLUS-Dist-Gentoo>.
713 I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
714
715 =head1 SUPPORT
716
717 You can find documentation for this module with the perldoc command.
718
719     perldoc CPANPLUS::Dist::Gentoo
720
721 =head1 ACKNOWLEDGEMENTS
722
723 The module was inspired by L<CPANPLUS::Dist::Deb> and L<CPANPLUS::Dist::Mdv>.
724
725 Kent Fredric, for testing and suggesting improvements.
726
727 =head1 COPYRIGHT & LICENSE
728
729 Copyright 2008,2009,2010 Vincent Pit, all rights reserved.
730
731 This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
732
733 =cut
734
735 1; # End of CPANPLUS::Dist::Gentoo