abuild/apkbuild-pypi.in

360 lines
9.2 KiB
Perl
Executable File

#!/usr/bin/perl
use strict;
use warnings;
use autodie qw(:all);
use feature qw(:5.14);
use LWP::UserAgent;
use LWP::ConnCache;
use JSON;
use URI;
our $packager = '';
my %pkgmap = ();
my %licenses = ();
my $template = <<'EOF';
# Automatically generated by apkbuild-pypi, template 4
[% authors %]
pkgname=[% pkgname %]
#_pkgreal is used by apkbuild-pypi to find modules at PyPI
_pkgreal=[% pkgreal %]
pkgver=[% pkgver %]
pkgrel=[% pkgrel %]
pkgdesc="[% pkgdesc %]"
provides="[% provides %]"
replaces="[% replaces %]"
url="http://packages.python.org/pypi/[% pkgreal %]"
arch="noarch"
license="[% license %]"
depends=""
makedepends="py3-gpep517 py3-setuptools py3-wheel"
checkdepends="py3-pytest"
subpackages="$pkgname-pyc"
source="[% source %]"
builddir="$srcdir/$_pkgreal-$pkgver"
build() {
gpep517 build-wheel \
--wheel-dir .dist \
--output-fd 3 3>&1 >&2
}
check() {
python3 -m venv --clear --without-pip --system-site-packages .testenv
.testenv/bin/python3 -m installer .dist/*.whl
.testenv/bin/python3 -m pytest
}
package() {
python3 -m installer -d "$pkgdir" \
.dist/*.whl
}
EOF
my $ua = LWP::UserAgent->new();
my $json = JSON->new;
$ua->env_proxy;
$ua->conn_cache(LWP::ConnCache->new);
sub read_file {
my ($filename) = @_;
open my $fh, '<:utf8', $filename;
local $/;
my $text = <$fh>;
return $text;
}
sub read_assignments_from_file {
my ($filename) = @_;
return () if ( ! -e $filename );
my $text = read_file($filename);
my %sline = $text =~ /^(\w+)\s*=\s*([^\"\n]*)$/mg;
my %mline = $text =~ /^(\w+)\s*=\s*\"([^\"]*)\".*$/mg;
my %hash = ( %sline, %mline );
my $authors = join("\n", $text =~ /^# Contributor: .*$/mg, $text =~ /^# Maintainer: .*$/mg);
$hash{'authors'} = $authors if length($authors) > 1;
my $provides = $text =~ m/provides=\"(.*)\"".*/mg;
$hash{'provides'} = $1 if length($provides) >= 1;
my $requires = $text =~ m/^requires=\"(.*)\"".*$/mg;
$hash{'requires'} = $1 if length($requires) >= 1;
return \%hash;
}
sub map_pypi_to_apk {
my ($pypi) = @_;
return $pkgmap{$pypi} unless !exists($pkgmap{$pypi});
return 'py3-'.lc($pypi);
}
sub map_license {
my ($license) = @_;
$license =~ s/ or / /g;
return $license;
}
sub get_source {
my ($distdata) = @_;
my $pkgname = $distdata->{info}{name};
my $source;
for my $url (@{$distdata->{urls}}) {
if ($url->{python_version} eq 'source') {
$source = URI->new($url->{url});
last;
}
}
die "Unable to locate sources for $pkgname.\n" unless $source;
my $filename = ($source->path_segments)[-1];
my $pretty_path = substr($pkgname, 0, 1) . "/$pkgname";
my $pretty_url = $source->clone;
$pretty_url->path("/packages/source/$pretty_path/$filename");
my $response = $ua->head($pretty_url);
if ($response->is_success) {
return $pretty_url->as_string;
} else {
return $source->as_string;
}
}
sub read_apkbuild {
return read_assignments_from_file('APKBUILD');
}
sub write_apkbuild {
my ($distdata, $apkbuild) = @_;
my $replaces = undef;
my $provides = undef;
my $authors = undef;
my $pkgrel = 0;
if (defined $apkbuild) {
$authors = $apkbuild->{authors};
$provides = $apkbuild->{provides};
$replaces = $apkbuild->{replaces};
$pkgrel = $apkbuild->{pkgrel};
if ($apkbuild->{pkgver} eq $distdata->{info}{version}) {
$pkgrel++;
}
}
my %repl = (
authors => ($authors or "# Contributor: $packager\n# Maintainer: $packager"),
pkgname => map_pypi_to_apk($distdata->{info}{name}),
pkgreal => $distdata->{info}{name},
pkgver => $distdata->{info}{version},
pkgrel => $pkgrel,
source => get_source($distdata),
license => map_license($distdata->{info}{license}),
pkgdesc => $distdata->{info}{summary},
provides => ($provides or ''),
replaces => ($replaces or ''),
);
$repl{source} =~ s/-$repl{pkgver}/-\$pkgver/g;
$template =~ s/\[% (.*?) %\]/$repl{$1}/g;
open my $fh, '>:utf8', 'APKBUILD';
print {$fh} $template;
close $fh;
say "Wrote $repl{pkgname}/APKBUILD";
return \%repl;
}
sub prepare_tree {
system('abuild checksum unpack prepare');
}
sub find_package_name {
my ($apkbuild) = @_;
my $pkgreal = '';
if (exists $apkbuild->{_realname}) {
$pkgreal = $apkbuild->{_realname};
} elsif (exists $apkbuild->{_pkgreal}) {
$pkgreal = $apkbuild->{_pkgreal};
} elsif (exists $apkbuild->{_pkgname}) {
$pkgreal = $apkbuild->{_pkgname};
} elsif (exists $apkbuild->{_name}) {
$pkgreal = $apkbuild->{_name};
} elsif (exists $apkbuild->{_realpkgname}) {
$pkgreal = $apkbuild->{_realpkgname};
} elsif (exists $apkbuild->{_pkg_real}) {
$pkgreal = $apkbuild->{_pkg_real};
} elsif (exists $apkbuild->{source}) {
$pkgreal = $apkbuild->{source};
$pkgreal =~ m/(\w+)-/;
$pkgreal = $1;
} else {
print "No pkg real found\n";
die;
}
return $pkgreal;
}
sub get_data {
my ($package) = @_;
my $response = $ua->get("https://pypi.python.org/pypi/$package/json");
$response->is_success or die $response->status_line;
my $distdata = $json->decode($response->decoded_content);
return $distdata;
}
sub get_deps {
my ($distdata, $data) = @_;
my $reqs = $distdata->{info}{requires_dist};
my %depends = ('py3-pytest' => 'py3-pytest');
my @reqs =
map {
my $apkname = map_pypi_to_apk(s/(\(|\s+).*$//r);
$depends{$apkname} = $apkname
}
grep { $_ !~ m/; extra ==/ }
@$reqs;
my @checkdeps =
map {
my $apkname = map_pypi_to_apk(s/(\(|\s+).*$//r);
exists($depends{$apkname}) ? () : $apkname
}
grep { m/; extra == 'tests'/ }
@$reqs;
my $apk = read_file('APKBUILD');
$reqs = join ' ', @reqs;
$apk =~ s/depends=""/depends="$reqs"/;
unshift @checkdeps, 'py3-pytest';
my $checkdeps = join ' ', @checkdeps;
$apk =~ s/checkdepends="py3-pytest"/checkdepends="$checkdeps"/;
# remove empty variables
$apk =~ s/.*=""\n//g;
open my $fh, '>:utf8', 'APKBUILD';
print $fh $apk;
say "Requires: @reqs\n\nCheckDepends: @checkdeps";
}
my $abuild_conf = read_assignments_from_file('/etc/abuild.conf');
$packager = $abuild_conf->{PACKAGER} if $abuild_conf->{PACKAGER};
my $user_abuild_conf = read_assignments_from_file($ENV{"HOME"} . "/.abuild/abuild.conf");
$packager = $user_abuild_conf->{PACKAGER} if $user_abuild_conf->{PACKAGER};
sub usage {
say <<'EOF';
Usage: apkbuild-pypi [create <package> | check | recreate | upgrade | update]
In the repository root:
create <package>: Creates an APKBUILD for <package>
In the package root:
check : Reports current & latest version of the package
recreate : Recreates the APKBUILD
upgrade : Upgrades to the latest version of the package
update : Updates APKBUILD metadata
EOF
}
if (! defined $ARGV[0]) {
die usage;
} elsif ($ARGV[0] eq 'create') {
my $package = $ARGV[1];
$package or die usage;
my $distdata = get_data($package);
my $apkname = map_pypi_to_apk($package);
mkdir $apkname;
chdir $apkname;
my $data = write_apkbuild($distdata, undef);
prepare_tree;
get_deps($distdata, $data);
} elsif ($ARGV[0] eq 'recreate') {
my $apkbuild = read_apkbuild;
if (! defined $apkbuild->{_pkgreal}) {
$apkbuild->{_pkgreal} = find_package_name($apkbuild);
}
my $distdata = get_data($apkbuild->{_pkgreal});
my $pkgver = $distdata->{info}{version} =~ s/^[^0-9]+//r;
if ($pkgver ne $apkbuild->{pkgver}) {
#Reset pkgrel on upgrade on recreate
say "Upgrading PyPI module from $apkbuild->{pkgver} to $pkgver";
$apkbuild->{pkgrel}=0;
}
my $data = write_apkbuild($distdata, $apkbuild);
prepare_tree;
get_deps($distdata, $data);
} elsif ($ARGV[0] eq 'upgrade') {
my $apkbuild = read_apkbuild;
if (! defined $apkbuild->{_pkgreal}) {
$apkbuild->{_pkgreal} = find_package_name($apkbuild);
}
my $distdata = get_data($apkbuild->{_pkgreal});
my $pkgver = $distdata->{info}{version};
if ($pkgver ne $apkbuild->{pkgver}) {
say "Upgrading PyPI package from $apkbuild->{pkgver} to $pkgver";
my $text = read_file('APKBUILD');
$text =~ s/^(pkgver)=.*$/$1=$pkgver/mg or
die "Can't find pkgver line in APKBUILD";
$text =~ s/^(pkgrel)=.*$/$1=0/mg or
die "Can't find pkgrel line in APKBUILD";
open my $fh, '>:utf8', 'APKBUILD';
print $fh $text;
close $fh;
} else {
say "Already up to date with PyPI";
}
} elsif ($ARGV[0] eq 'check') {
my $apkbuild = read_apkbuild;
if (! defined $apkbuild->{_pkgreal}) {
$apkbuild->{_pkgreal} = find_package_name($apkbuild);
}
my $distdata = get_data($apkbuild->{_pkgreal});
my $pkgver = $distdata->{info}{version};
say "$apkbuild->{pkgname}: Latest version: $pkgver Packaged version: $apkbuild->{pkgver}";
if ($pkgver ne $apkbuild->{pkgver}) {
exit(1);
}
} elsif ($ARGV[0] eq 'update') {
prepare_tree;
} else {
die usage;
}