1 package CPANPLUS::Dist::Gentoo;
11 use IPC::Cmd qw/run can_run/;
13 use CPANPLUS::Error ();
15 use base qw/CPANPLUS::Dist::Base/;
17 use CPANPLUS::Dist::Gentoo::Maps;
21 CPANPLUS::Dist::Gentoo - CPANPLUS backend generating Gentoo ebuilds.
29 our $VERSION = '0.07';
33 cpan2dist --format=CPANPLUS::Dist::Gentoo \
34 --dist-opts overlay=/usr/local/portage \
35 --dist-opts distdir=/usr/portage/distfiles \
36 --dist-opts manifest=yes \
37 --dist-opts keywords=x86 \
38 --dist-opts header="# Copyright 1999-2008 Gentoo Foundation" \
39 --dist-opts footer="# End" \
44 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.
45 You need write permissions on the directory where Gentoo fetches its source files (usually F</usr/portage/distfiles>).
46 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.
48 The generated ebuilds are placed into the C<perl-gcpanp> category.
49 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 After installing this module, you should append C<perl-gcpanp> to your F</etc/portage/categories> file.
57 This module inherits all the methods from L<CPANPLUS::Dist::Base>.
58 Please refer to its documentation for precise information on what's done at each step.
62 use constant CATEGORY => 'perl-gcpanp';
80 sub format_available {
81 return $format_available if defined $format_available;
83 for my $prog (qw/emerge ebuild/) {
84 unless (can_run($prog)) {
85 __PACKAGE__->_abort("$prog is required to write ebuilds");
86 return $format_available = 0;
90 if (IPC::Cmd->can_capture_buffer) {
92 my ($success, $errmsg) = run command => [ qw/emerge --info/ ],
96 if ($buffers =~ /^PORTDIR_OVERLAY=(.*)$/m) {
97 $overlays = [ map abs_path($_), split ' ', $unquote->($1) ];
99 if ($buffers =~ /^ACCEPT_KEYWORDS=(.*)$/m) {
100 $default_keywords = [ split ' ', $unquote->($1) ];
102 if ($buffers =~ /^DISTDIR=(.*)$/m) {
103 $default_distdir = abs_path($unquote->($1));
105 if ($buffers =~ /^PORTDIR=(.*)$/m) {
106 $main_portdir = abs_path($unquote->($1));
109 __PACKAGE__->_abort($errmsg);
113 $default_keywords = [ 'x86' ] unless defined $default_keywords;
114 $default_distdir = '/usr/portage/distfiles' unless defined $default_distdir;
116 return $format_available = 1;
121 my $stat = $self->status;
122 my $conf = $self->parent->parent->configure_object;
124 $stat->mk_accessors(qw/name version author distribution desc uri src license
126 ebuild_name ebuild_version ebuild_dir ebuild_file
128 overlay distdir keywords do_manifest header footer
131 $stat->force($conf->get_conf('force'));
132 $stat->verbose($conf->get_conf('verbose'));
139 my $mod = $self->parent;
140 my $stat = $self->status;
141 my $int = $mod->parent;
142 my $conf = $int->configure_object;
146 my $OK = sub { $stat->prepared(1); 1 };
147 my $FAIL = sub { $stat->prepared(0); $self->_abort(@_) if @_; 0 };
149 my $keywords = delete $opts{'keywords'};
150 if (defined $keywords) {
151 $keywords = [ split ' ', $keywords ];
153 $keywords = $default_keywords;
155 $stat->keywords($keywords);
157 my $manifest = delete $opts{'manifest'};
158 $manifest = 1 unless defined $manifest;
159 $manifest = 0 if $manifest =~ /^\s*no?\s*$/i;
160 $stat->do_manifest($manifest);
162 my $header = delete $opts{'header'};
163 if (defined $header) {
164 1 while chomp $header;
169 $stat->header($header);
171 my $footer = delete $opts{'footer'};
172 if (defined $footer) {
173 $footer = "\n" . $footer;
177 $stat->footer($footer);
179 my $overlay = delete $opts{'overlay'};
180 $overlay = (defined $overlay) ? abs_path $overlay : '/usr/local/portage';
181 $stat->overlay($overlay);
183 my $distdir = delete $opts{'distdir'};
184 $distdir = (defined $distdir) ? abs_path $distdir : $default_distdir;
185 $stat->distdir($distdir);
187 if ($stat->do_manifest && !-w $stat->distdir) {
188 return $FAIL->('distdir isn\'t writable');
190 $stat->fetched_arch($mod->status->fetch);
192 my $cur = File::Spec->curdir();
195 if ($_ eq $overlay or File::Spec->abs2rel($overlay, $_) eq $cur) {
196 $portdir_overlay = [ @$overlays ];
200 $portdir_overlay = [ @$overlays, $overlay ] unless $portdir_overlay;
201 $stat->portdir_overlay($portdir_overlay);
203 my $name = $mod->package_name;
206 my $version = $mod->package_version;
207 $stat->version($version);
209 my $author = $mod->author->cpanid;
210 $stat->author($author);
212 $stat->distribution($name . '-' . $version);
214 $stat->ebuild_version(CPANPLUS::Dist::Gentoo::Maps::version_c2g($version));
216 $stat->ebuild_name(CPANPLUS::Dist::Gentoo::Maps::name_c2g($name));
218 $stat->ebuild_dir(File::Spec->catdir(
224 my $file = File::Spec->catfile(
226 $stat->ebuild_name . '-' . $stat->ebuild_version . '.ebuild',
228 $stat->ebuild_file($file);
232 if ($stat->force and not $forced{$file}) {
234 1 while unlink $file;
238 $self->_skip("Can't force rewriting of $file");
241 $self->_skip('Ebuild already generated for', $stat->distribution);
251 $self->SUPER::prepare(%opts);
255 my $desc = $mod->description;
256 ($desc = $name) =~ s/-+/::/g unless $desc;
259 $stat->uri('http://search.cpan.org/dist/' . $name);
261 unless ($author =~ /^(.)(.)/) {
262 return $FAIL->('Wrong author name');
264 $stat->src("mirror://cpan/modules/by-authors/id/$1/$1$2/$author/"
267 $stat->license([ qw/Artistic GPL-2/ ]);
269 my $prereqs = $mod->status->prereqs;
271 for my $prereq (sort keys %$prereqs) {
272 next if $prereq =~ /^perl(?:-|\z)/;
273 my $obj = $int->module_tree($prereq);
274 return $FAIL->('Wrong module object') unless $obj;
275 next if $obj->package_is_perl_core;
278 if ($prereqs->{$prereq}) {
279 if ($obj->installed_version && $obj->installed_version < $obj->version) {
280 $version = $obj->installed_version;
282 $version = $obj->package_version;
285 push @depends, [ $obj->package_name, $version ];
288 $stat->deps(\@depends);
295 my $stat = $self->status;
297 my $OK = sub { $stat->created(1); $stat->dist($stat->ebuild_file); 1 };
298 my $FAIL = sub { $stat->created(0); $stat->dist(undef); $self->_abort(@_) if @_; 0 };
300 unless ($stat->prepared) {
302 'Can\'t create', $stat->distribution, 'since it was never prepared'
306 if ($stat->created) {
307 $self->_skip($stat->distribution, 'was already created');
311 my $dir = $stat->ebuild_dir;
313 eval { File::Path::mkpath($dir) };
314 return $FAIL->("mkpath($dir): $@") if $@;
317 my $file = $stat->ebuild_file;
318 open my $eb, '>', $file or return $FAIL->("open($file): $!");
319 print $eb $self->ebuild_source;
325 $self->SUPER::create(@_);
327 if ($stat->do_manifest and not $self->update_manifest) {
328 1 while unlink $file;
335 =head2 C<update_manifest>
337 Updates the F<Manifest> file for the ebuild associated to the current dist object.
341 sub update_manifest {
343 my $stat = $self->status;
345 my $file = $stat->ebuild_file;
346 unless ($file and -e $file) {
347 return $self->_abort('The ebuild file is invalid or does not exist');
350 unless (File::Copy::copy($stat->fetched_arch => $stat->distdir)) {
351 return $self->_abort("Couldn\'t copy the distribution file to distdir ($!)");
354 $self->_notify('Adding Manifest entry for', $stat->distribution);
356 return $self->_run([ 'ebuild', $stat->ebuild_file, 'manifest' ], 0);
359 =head2 C<ebuild_source>
361 Returns the source of the ebuild for the current dist object.
367 my $stat = $self->status;
369 # We must resolve the deps now and not inside prepare because _cpan2portage
370 # has to see the ebuilds already generated for the dependencies of the current
374 sort grep !$seen{$_}++, 'dev-lang/perl',
375 map $self->_cpan2portage(@$_), @{$stat->deps}
378 my $d = $stat->header;
379 $d .= "# Generated by CPANPLUS::Dist::Gentoo version $VERSION\n\n";
380 $d .= 'MODULE_AUTHOR="' . $stat->author . "\"\ninherit perl-module\n\n";
381 $d .= 'S="${WORKDIR}/' . $stat->distribution . "\"\n";
382 $d .= 'DESCRIPTION="' . $stat->desc . "\"\n";
383 $d .= 'HOMEPAGE="' . $stat->uri . "\"\n";
384 $d .= 'SRC_URI="' . $stat->src . "\"\n";
385 $d .= "SLOT=\"0\"\n";
386 $d .= 'LICENSE="|| ( ' . join(' ', sort @{$stat->license}) . " )\"\n";
387 $d .= 'KEYWORDS="' . join(' ', sort @{$stat->keywords}) . "\"\n";
388 $d .= 'DEPEND="' . join("\n", @deps) . "\"\n";
389 $d .= "SRC_TEST=\"do\"\n";
396 my ($self, $name, $version) = @_;
398 $name = CPANPLUS::Dist::Gentoo::Maps::name_c2g($name);
400 $ver = CPANPLUS::Dist::Gentoo::Maps::version_c2g($version) if defined $version;
402 my @portdirs = ($main_portdir, @{$self->status->portdir_overlay});
404 for my $category (qw/virtual perl-core dev-perl perl-gcpan/, CATEGORY) {
405 my $atom = ($category eq 'virtual' ? 'perl-' : '') . $name;
407 for my $portdir (@portdirs) {
408 my @ebuilds = glob File::Spec->catfile(
415 if (defined $ver) { # implies that $version is defined
417 my ($eb_ver) = /\Q$atom\E-v?([\d._pr-]+).*?\.ebuild$/;
418 return ">=$category/$atom-$ver"
420 and CPANPLUS::Dist::Gentoo::Maps::version_gcmp($eb_ver, $ver) > 0;
423 return "$category/$atom";
431 "Couldn't find an appropriate ebuild for $name in the portage tree"
439 my $stat = $self->status;
440 my $conf = $self->parent->parent->configure_object;
442 my $sudo = $conf->get_program('sudo');
443 my @cmd = ('emerge', '=' . $stat->ebuild_name . '-' . $stat->ebuild_version);
444 unshift @cmd, $sudo if $sudo;
446 my $success = $self->_run(\@cmd, 1);
447 $stat->installed($success);
454 my $stat = $self->status;
455 my $conf = $self->parent->parent->configure_object;
457 my $sudo = $conf->get_program('sudo');
458 my @cmd = ('emerge', '-C', '=' . $stat->ebuild_name . '-' . $stat->ebuild_version);
459 unshift @cmd, $sudo if $sudo;
461 my $success = $self->_run(\@cmd, 1);
462 $stat->uninstalled($success);
468 my ($self, $cmd, $verbose) = @_;
469 my $stat = $self->status;
471 my ($success, $errmsg, $output) = do {
472 local $ENV{PORTDIR_OVERLAY} = join ' ', @{$stat->portdir_overlay};
473 local $ENV{PORTAGE_RO_DISTDIRS} = $stat->distdir;
474 run command => $cmd, verbose => $verbose;
478 $self->_abort($errmsg);
479 if (not $verbose and defined $output and $stat->verbose) {
480 my $msg = join '', @$output;
482 CPANPLUS::Error::error($msg);
492 CPANPLUS::Error::error("@_ -- aborting");
500 CPANPLUS::Error::msg("@_");
505 sub _skip { shift->_notify(@_, '-- skipping') }
509 Gentoo (L<http://gentoo.org>).
511 L<CPANPLUS>, L<IPC::Cmd> (core modules since 5.9.5).
513 L<Cwd>, L<Carp> (since perl 5), L<File::Path> (5.001), L<File::Copy> (5.002), L<File::Spec> (5.00405).
519 L<CPANPLUS::Dist::Base>, L<CPANPLUS::Dist::Deb>, L<CPANPLUS::Dist::Mdv>.
523 Vincent Pit, C<< <perl at profvince.com> >>, L<http://www.profvince.com>.
525 You can contact me by mail or on C<irc.perl.org> (vincent).
529 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>.
530 I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
534 You can find documentation for this module with the perldoc command.
536 perldoc CPANPLUS::Dist::Gentoo
538 =head1 ACKNOWLEDGEMENTS
540 The module was inspired by L<CPANPLUS::Dist::Deb> and L<CPANPLUS::Dist::Mdv>.
542 Kent Fredric, for testing and suggesting improvements.
544 =head1 COPYRIGHT & LICENSE
546 Copyright 2008-2009 Vincent Pit, all rights reserved.
548 This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
552 1; # End of CPANPLUS::Dist::Gentoo