ports/infrastructure/lib/OpenBSD/PortGen/Port/CPAN.pm

298 lines
6.3 KiB
Perl

# $OpenBSD: CPAN.pm,v 1.9 2021/06/13 19:00:06 afresh1 Exp $
#
# Copyright (c) 2015 Giannis Tsaraias <tsg@openbsd.org>
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
package OpenBSD::PortGen::Port::CPAN;
use 5.012;
use warnings;
use parent 'OpenBSD::PortGen::Port';
use File::Find qw( find );
use OpenBSD::PortGen::Dependency;
use OpenBSD::PortGen::Utils qw( fetch module_in_ports );
sub ecosystem_prefix
{
my $self = shift;
return 'p5-';
}
sub base_url
{
my $self = shift;
return 'https://fastapi.metacpan.org/v1/';
}
sub get_dist_info
{
my ( $self, $module ) = @_;
my $dist = $self->get_dist_for_module($module);
return $self->get_json("release/$dist");
}
sub get_ver_info
{
return 1;
}
sub get_dist_for_module
{
my ( $self, $module ) = @_;
return $self->get_json("module/$module?fields=distribution")
->{distribution};
}
sub name_new_port
{
my ( $self, $di ) = @_;
my $name = $self->SUPER::name_new_port(
ref $di ? $di->{metadata}->{name} : $di );
$name =~ s/::/-/g;
$name = "cpan/$name" unless $name =~ m{/};
return $name;
}
sub needs_author
{
my ( $self, $di ) = @_;
my $mirror = 'http://www.cpan.org';
# one module used 'src/foo.tar.gz', we only need foo.tar.gz
my ($file) = $di->{archive} =~ /([^\/]+)$/;
$file = "$1/$file" if $file =~ /^(\w+)-/;
return !fetch("$mirror/modules/by-module/$file");
}
sub get_config_style
{
my ( $self, $di, $wrksrc ) = @_;
if ( $di->{metadata}->{generated_by} =~ /Module::Install/
|| -e "$wrksrc/inc/Module/Install.pm" )
{
return 'modinst';
}
for my $dep ( @{ $di->{dependency} } ) {
return 'modbuild' if $dep->{module} eq 'Module::Build';
return 'modbuild tiny'
if $dep->{module} eq 'Module::Build::Tiny';
}
return;
}
sub get_deps
{
my ( $self, $di, $wrksrc ) = @_;
my $deps = OpenBSD::PortGen::Dependency->new();
if ( -e "$wrksrc/MYMETA.json" ) {
my $meta = $self->get_json_file("$wrksrc/MYMETA.json");
$di = $meta if defined $meta;
} else {
$di = $di->{metadata};
}
for my $phase (qw/ build runtime configure test /) {
next unless $di->{prereqs}{$phase};
for my $relation (qw/ requires recommends /) {
next if $relation eq 'recommends' and $phase ne 'test';
while ( my ( $module, $req ) =
each %{ $di->{prereqs}{$phase}{$relation} } )
{
next if $self->is_in_base($module);
next if $module eq 'Module::Build::Tiny';
my $dist = $self->get_dist_for_module($module);
my $port = module_in_ports( $dist, 'p5-' )
|| $self->name_new_port($dist);
$req =~ s/^v//;
$req =~ s/0+$/0/;
if ( $phase eq 'configure'
|| $phase eq 'build' )
{
$deps->add_build( $port, ">=$req" );
} elsif ( $phase eq 'runtime' ) {
$deps->add_run( $port, ">=$req" );
} elsif ( $phase eq 'test' ) {
$deps->add_test( $port, ">=$req" );
}
# don't have it in tree, port it
if ( $port =~ m{^cpan/} ) {
my $o =
OpenBSD::PortGen::Port::CPAN->new();
$o->port($module);
$self->add_notice( $o->notices );
}
}
}
}
return $deps->format();
}
sub read_descr
{
my ( $self, $path ) = @_;
open my $readme, '<', "$path/README" or return;
my $descr = do { local $/ = undef; <$readme> };
if ( $descr =~ /^DESCRIPTION\n(.+?)^\p{Upper}+/ms ) {
return $1 unless $1 =~ /^\s+$/;
}
if ( $descr =~ /^SYNOPSIS\n(.+?)^\p{Upper}+/ms ) {
return $1 unless $1 =~ /^\s+$/;
}
return;
}
sub fill_in_makefile
{
my ( $self, $di, $vi ) = @_;
$self->set_comment( $di->{abstract} );
$self->pick_distfile( $di->{archive} );
$self->set_license(
$di->{metadata}->{license}[0] eq 'unknown'
? ''
: $di->{metadata}->{license}[0]
);
$self->set_modules('cpan');
# It's common for perl ports to have a version that starts with a v.
# However, that's not a valid PKGNAME.
$self->set_other( PKGNAME => 'p5-${DISTNAME:S/-v/-/}' )
if not $self->get_other('PKGNAME')
and ( $di->{version} || '' ) =~ /^v/;
$self->set_other( 'CPAN_AUTHOR', $di->{author} )
if $self->needs_author($di);
}
sub postextract
{
my ( $self, $di, $wrksrc ) = @_;
$self->set_descr( $self->read_descr($wrksrc) || $di->{abstract} );
$self->_find_hidden_test_deps($wrksrc);
if ( $self->_uses_xs($wrksrc) ) {
$self->set_other( 'WANTLIB', 'perl' );
} else {
$self->set_other( 'PKG_ARCH', '*' );
}
}
sub try_building
{
my $self = shift;
if ( $self->make_fake() and $self->get_other('CONFIGURE_STYLE') ) {
warn
"* * * Warning: failed to build with CONFIGURE_STYLE, trying without * * *";
$self->set_other( 'CONFIGURE_STYLE', undef );
$self->write_makefile();
$self->make_clean();
!$self->make_fake() or die 'cannot build port';
}
}
sub is_in_base
{
my ( $self, $module ) = @_;
return 1 if $module eq 'perl';
$module .= '.pm';
$module =~ s{::}{/}g;
for (@INC) {
next unless m{/usr/libdata/};
return 1 if -e "$_/$module";
}
return;
}
# Some testfiles skip tests because the required modules are not
# available. Those modules are not always listed as dependencies, so
# manually check if there are testfiles skipping tests and warn the
# porter about them.
sub _find_hidden_test_deps
{
my ( $self, $path ) = @_;
return unless -d "$path/t";
find( sub { $self->_test_skips($File::Find::name) }, "$path/t" );
}
sub _test_skips
{
my ( $self, $file ) = @_;
# not a testfile
return unless $file =~ /\.t$/;
open my $fh, '<', $file or die $!;
while (<$fh>) {
if (/plan skip_all/) {
$self->add_notice(
"Possible hidden test dependency in: $file");
return;
}
}
}
sub _uses_xs
{
my ( $self, $dir ) = @_;
my $found_xs = 0;
find(
sub {
if ( -d && /^(inc|t|xt)$/ ) {
$File::Find::prune = 1;
return;
}
$found_xs = 1 if -f && /\.xs$/;
},
$dir
);
return $found_xs;
}
1;